using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace CPF.Windows.Json.Deserialize { internal class InterfaceImplementation { public static readonly Type Proxy; static InterfaceImplementation() { try { var iType = typeof(TInterface); Random r = new Random(); var className = iType.Name + "Impl" + r.Next(int.MinValue, int.MaxValue).ToString(); while (AssemblyBuilderContainer.TypeNames.Contains(className)) className = iType.Name + "Impl" + r.Next(int.MinValue, int.MaxValue).ToString(); var typeBuilder = AssemblyBuilderContainer.ModBuilder.DefineType( className, TypeAttributes.Class | TypeAttributes.Sealed, typeof(object), new[] { iType } ); var allMembers = iType.GetAllInterfaceMembers(); List propertyInfos = new List(); foreach (var prop in allMembers.OfType()) { var propType = ReturnType(prop); var propBuilder = typeBuilder.DefineProperty(prop.Name, prop.Attributes, propType, Type.EmptyTypes); var iGetter = prop.GetMethod; var iSetter = prop.SetMethod; if (iGetter != null) propertyInfos.Add(iGetter); if (iSetter != null) propertyInfos.Add(iSetter); if (prop.Name == "Item") { if (iGetter != null) { var accessor = iGetter.Attributes; accessor &= ~MethodAttributes.Abstract; var methBuilder = typeBuilder.DefineMethod(iGetter.Name, accessor, iGetter.ReturnType, iGetter.GetParameters().Select(e => e.ParameterType).ToArray()); var il = methBuilder.GetILGenerator(); il.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructors()[0]); il.Emit(OpCodes.Throw); propBuilder.SetGetMethod(methBuilder); } if (iSetter != null) { var accessor = iSetter.Attributes; accessor &= ~MethodAttributes.Abstract; var methBuilder = typeBuilder.DefineMethod(iSetter.Name, accessor, iSetter.ReturnType, iSetter.GetParameters().Select(e => e.ParameterType).ToArray()); var il = methBuilder.GetILGenerator(); il.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructors()[0]); il.Emit(OpCodes.Throw); propBuilder.SetSetMethod(methBuilder); } continue; } Func getBackingField; { FieldInfo backingField = null; getBackingField = () => { if (backingField == null) { backingField = typeBuilder.DefineField("_" + prop.Name + "_" + Guid.NewGuid(), ReturnType(prop), FieldAttributes.Private); } return backingField; }; } if (iGetter != null) { var accessor = iGetter.Attributes; accessor &= ~MethodAttributes.Abstract; var methBuilder = typeBuilder.DefineMethod(iGetter.Name, accessor, propType, Type.EmptyTypes); var il = methBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, getBackingField()); il.Emit(OpCodes.Ret); propBuilder.SetGetMethod(methBuilder); } if (iGetter != null || iSetter != null) { var accessor = iSetter != null ? iSetter.Attributes : MethodAttributes.Private; var name = iSetter != null ? iSetter.Name : "set_" + prop.Name; accessor &= ~MethodAttributes.Abstract; var methBuilder = typeBuilder.DefineMethod(name, accessor, typeof(void), new[] { propType }); var il = methBuilder.GetILGenerator(); if (iGetter != null) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, getBackingField()); il.Emit(OpCodes.Ret); } else { il.Emit(OpCodes.Ret); } propBuilder.SetSetMethod(methBuilder); } } foreach (var method in allMembers.OfType().Except(propertyInfos)) { var methBuilder = typeBuilder.DefineMethod(method.Name, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, method.ReturnType, method.GetParameters().Select(e => e.ParameterType).ToArray()); if (method.IsGenericMethod) { methBuilder.DefineGenericParameters(method.GetGenericArguments().Select(e => e.Name).ToArray()); } var il = methBuilder.GetILGenerator(); il.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructors()[0]); il.Emit(OpCodes.Throw); typeBuilder.DefineMethodOverride(methBuilder, method); } var proxyInfo = typeBuilder.CreateTypeInfo(); Proxy = proxyInfo.AsType(); AssemblyBuilderContainer.TypeNames.Add(className); } catch (TypeLoadException) { throw new JsonNoSupportedDeserializeTypeException(typeof(TInterface), "Interface access properties must be public"); } catch { throw new JsonNoSupportedDeserializeTypeException(typeof(TInterface)); } } static Type ReturnType(MemberInfo m) { var asField = m as FieldInfo; var asProp = m as PropertyInfo; return asField != null ? asField.FieldType : asProp.PropertyType; } } static class AssemblyBuilderContainer { private static readonly AssemblyBuilder AsmBuilder; internal static readonly ModuleBuilder ModBuilder; internal static List TypeNames = new List(); static AssemblyBuilderContainer() { AsmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("KoobooInterfaceTypeProxies"), AssemblyBuilderAccess.Run); ModBuilder = AsmBuilder.DefineDynamicModule("InterfaceProxies"); } } }