private static Type EmitProxy(TypeDescription typeDescription) { var interfaceType = typeDescription.Type; var additionalProperties = typeDescription.AdditionalProperties; var propertyNames = string.Join("_", additionalProperties.Select(p => p.Name)); string name = $"Proxy{propertyNames}<{Regex.Replace(interfaceType.AssemblyQualifiedName ?? interfaceType.FullName ?? interfaceType.Name, @"[\s,]+", "_")}>"; var allInterfaces = new List <Type> { interfaceType }; allInterfaces.AddRange(interfaceType.GetTypeInfo().ImplementedInterfaces); Debug.WriteLine(name, "Emitting proxy type"); TypeBuilder typeBuilder = proxyModule.DefineType(name, TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public, typeof(ProxyBase), interfaceType.IsInterface ? new[] { interfaceType } : Type.EmptyTypes); ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[0]); ILGenerator ctorIl = constructorBuilder.GetILGenerator(); ctorIl.Emit(OpCodes.Ldarg_0); ctorIl.Emit(OpCodes.Call, proxyBase_ctor); ctorIl.Emit(OpCodes.Ret); FieldBuilder propertyChangedField = null; if (typeof(INotifyPropertyChanged).IsAssignableFrom(interfaceType)) { propertyChangedField = typeBuilder.DefineField("PropertyChanged", typeof(PropertyChangedEventHandler), FieldAttributes.Private); MethodBuilder addPropertyChangedMethod = typeBuilder.DefineMethod("add_PropertyChanged", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual, typeof(void), new[] { typeof(PropertyChangedEventHandler) }); ILGenerator addIl = addPropertyChangedMethod.GetILGenerator(); addIl.Emit(OpCodes.Ldarg_0); addIl.Emit(OpCodes.Dup); addIl.Emit(OpCodes.Ldfld, propertyChangedField); addIl.Emit(OpCodes.Ldarg_1); addIl.Emit(OpCodes.Call, delegate_Combine); addIl.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler)); addIl.Emit(OpCodes.Stfld, propertyChangedField); addIl.Emit(OpCodes.Ret); MethodBuilder removePropertyChangedMethod = typeBuilder.DefineMethod("remove_PropertyChanged", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual, typeof(void), new[] { typeof(PropertyChangedEventHandler) }); ILGenerator removeIl = removePropertyChangedMethod.GetILGenerator(); removeIl.Emit(OpCodes.Ldarg_0); removeIl.Emit(OpCodes.Dup); removeIl.Emit(OpCodes.Ldfld, propertyChangedField); removeIl.Emit(OpCodes.Ldarg_1); removeIl.Emit(OpCodes.Call, delegate_Remove); removeIl.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler)); removeIl.Emit(OpCodes.Stfld, propertyChangedField); removeIl.Emit(OpCodes.Ret); typeBuilder.DefineMethodOverride(addPropertyChangedMethod, iNotifyPropertyChanged_PropertyChanged.AddMethod); typeBuilder.DefineMethodOverride(removePropertyChangedMethod, iNotifyPropertyChanged_PropertyChanged.RemoveMethod); } var propertiesToImplement = new List <PropertyDescription>(); // first we collect all properties, those with setters before getters in order to enable less specific redundant getters foreach (var property in allInterfaces.Where(intf => intf != typeof(INotifyPropertyChanged)) .SelectMany(intf => intf.GetProperties()) .Select(p => new PropertyDescription(p)) .Concat(additionalProperties)) { if (property.CanWrite) { propertiesToImplement.Insert(0, property); } else { propertiesToImplement.Add(property); } } var fieldBuilders = new Dictionary <string, PropertyEmitter>(); foreach (var property in propertiesToImplement) { PropertyEmitter propertyEmitter; if (fieldBuilders.TryGetValue(property.Name, out propertyEmitter)) { if ((propertyEmitter.PropertyType != property.Type) && ((property.CanWrite) || (!property.Type.IsAssignableFrom(propertyEmitter.PropertyType)))) { throw new ArgumentException( $"The interface has a conflicting property {property.Name}", nameof(interfaceType)); } } else { fieldBuilders.Add(property.Name, propertyEmitter = new PropertyEmitter(typeBuilder, property, propertyChangedField)); } } return(typeBuilder.CreateType()); }
private static Type EmitProxy(TypeDescription typeDescription) { var interfaceType = typeDescription.Type; var typeBuilder = GenerateType(); GenerateConstructor(); FieldBuilder propertyChangedField = null; if (typeof(INotifyPropertyChanged).IsAssignableFrom(interfaceType)) { GeneratePropertyChanged(); } GenerateFields(); return(typeBuilder.CreateTypeInfo().AsType()); TypeBuilder GenerateType() { var propertyNames = string.Join("_", typeDescription.AdditionalProperties.Select(p => p.Name)); var typeName = $"Proxy_{interfaceType.FullName}_{typeDescription.GetHashCode()}_{propertyNames}"; const int MaxTypeNameLength = 1023; typeName = typeName.Substring(0, Math.Min(MaxTypeNameLength, typeName.Length)); Debug.WriteLine(typeName, "Emitting proxy type"); return(ProxyModule.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public, typeof(ProxyBase), interfaceType.IsInterface ? new[] { interfaceType } : Type.EmptyTypes)); } void GeneratePropertyChanged() { propertyChangedField = typeBuilder.DefineField(PropertyChanged.Name, typeof(PropertyChangedEventHandler), FieldAttributes.Private); EventAccessor(PropertyChanged.AddMethod, DelegateCombine); EventAccessor(PropertyChanged.RemoveMethod, DelegateRemove); } void EventAccessor(MethodInfo method, MethodInfo delegateMethod) { var eventAccessor = typeBuilder.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual, typeof(void), new[] { typeof(PropertyChangedEventHandler) }); var addIl = eventAccessor.GetILGenerator(); addIl.Emit(OpCodes.Ldarg_0); addIl.Emit(OpCodes.Dup); addIl.Emit(OpCodes.Ldfld, propertyChangedField); addIl.Emit(OpCodes.Ldarg_1); addIl.Emit(OpCodes.Call, delegateMethod); addIl.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler)); addIl.Emit(OpCodes.Stfld, propertyChangedField); addIl.Emit(OpCodes.Ret); typeBuilder.DefineMethodOverride(eventAccessor, method); } void GenerateFields() { var fieldBuilders = new Dictionary <string, PropertyEmitter>(); foreach (var property in PropertiesToImplement()) { if (fieldBuilders.TryGetValue(property.Name, out var propertyEmitter)) { if (propertyEmitter.PropertyType != property.Type && (property.CanWrite || !property.Type.IsAssignableFrom(propertyEmitter.PropertyType))) { throw new ArgumentException($"The interface has a conflicting property {property.Name}", nameof(interfaceType)); } } else { fieldBuilders.Add(property.Name, new PropertyEmitter(typeBuilder, property, propertyChangedField)); } } } List <PropertyDescription> PropertiesToImplement() { var propertiesToImplement = new List <PropertyDescription>(); var allInterfaces = new List <Type>(interfaceType.GetInterfaces()) { interfaceType }; // first we collect all properties, those with setters before getters in order to enable less specific redundant getters foreach (var property in allInterfaces.Where(intf => intf != typeof(INotifyPropertyChanged)) .SelectMany(intf => intf.GetProperties()) .Select(p => new PropertyDescription(p)) .Concat(typeDescription.AdditionalProperties)) { if (property.CanWrite) { propertiesToImplement.Insert(0, property); } else { propertiesToImplement.Add(property); } } return(propertiesToImplement); } void GenerateConstructor() { var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); var ctorIl = constructorBuilder.GetILGenerator(); ctorIl.Emit(OpCodes.Ldarg_0); ctorIl.Emit(OpCodes.Call, ProxyBaseCtor); ctorIl.Emit(OpCodes.Ret); } }