private static Type EmitProxy()

in tools/AutoMapper/Execution/ProxyGenerator.cs [49:143]


        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 } : new Type[0]);
            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.GetAddMethod());
                typeBuilder.DefineMethodOverride(removePropertyChangedMethod,
                    iNotifyPropertyChanged_PropertyChanged.GetRemoveMethod());
            }
            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();
        }