Ejemplo n.º 1
0
        public static Type CreateClassProxy(Type serviceType, Type implementType)
        {
            if (null == serviceType)
            {
                throw new ArgumentNullException(nameof(serviceType));
            }
            if (null == implementType)
            {
                implementType = serviceType;
            }
            if (serviceType.IsSealed || implementType.IsSealed)
            {
                throw new InvalidOperationException("the class type is sealed");
            }
            //
            var proxyTypeName = _proxyTypeNameResolver(serviceType, implementType);

            if (_proxyTypes.TryGetValue(proxyTypeName, out var proxyType))
            {
                return(proxyType);
            }

            lock (_typeLock)
            {
                if (_proxyTypes.TryGetValue(proxyTypeName, out proxyType))
                {
                    return(proxyType);
                }

                var typeBuilder = _moduleBuilder.DefineType(proxyTypeName, TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, implementType, Type.EmptyTypes);
                GenericParameterUtils.DefineGenericParameter(implementType, typeBuilder);

                var targetField = typeBuilder.DefineField(TargetFieldName, implementType, FieldAttributes.Private);

                // constructors
                var constructors = implementType.GetConstructors();
                if (constructors.Length > 0)
                {
                    foreach (var constructor in constructors)
                    {
                        var constructorTypes   = constructor.GetParameters().Select(o => o.ParameterType).ToArray();
                        var constructorBuilder = typeBuilder.DefineConstructor(
                            constructor.Attributes,
                            constructor.CallingConvention,
                            constructorTypes);
                        foreach (var customAttribute in constructor.CustomAttributes)
                        {
                            constructorBuilder.SetCustomAttribute(DefineCustomAttribute(customAttribute));
                        }

                        var il = constructorBuilder.GetILGenerator();

                        il.EmitThis();
                        for (var i = 0; i < constructorTypes.Length; i++)
                        {
                            il.Emit(OpCodes.Ldarg, i + 1);
                        }

                        il.Call(constructor);
                        il.Emit(OpCodes.Nop);

                        il.EmitThis();
                        il.EmitThis();
                        il.Emit(OpCodes.Stfld, targetField);

                        il.Emit(OpCodes.Ret);
                    }
                }
                else
                {
                    var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes);
                    var il = constructorBuilder.GetILGenerator();

                    il.EmitThis();
                    il.EmitThis();
                    il.Emit(OpCodes.Stfld, targetField);

                    il.Emit(OpCodes.Ret);
                }
                // properties
                var propertyMethods = new HashSet <string>();
                foreach (var property in serviceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                {
                    if (property.IsVisibleAndVirtual())
                    {
                        var propertyBuilder = typeBuilder.DefineProperty(property.Name, property.Attributes, property.PropertyType, Type.EmptyTypes);

                        //inherit targetMethod's attribute
                        foreach (var customAttributeData in property.CustomAttributes)
                        {
                            propertyBuilder.SetCustomAttribute(DefineCustomAttribute(customAttributeData));
                        }

                        if (property.CanRead)
                        {
                            propertyMethods.Add(property.GetMethod.Name);

                            var method = MethodUtils.DefineClassMethod(typeBuilder, property.GetMethod, targetField);
                            propertyBuilder.SetGetMethod(method);
                        }
                        if (property.CanWrite)
                        {
                            propertyMethods.Add(property.SetMethod.Name);

                            var method = MethodUtils.DefineClassMethod(typeBuilder, property.SetMethod, targetField);
                            propertyBuilder.SetSetMethod(method);
                        }
                    }
                }

                // methods
                var methods = serviceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                              .Where(m => m.IsVirtual && !m.IsFinal && m.IsVisible() && !propertyMethods.Contains(m.Name) && !_ignoredMethods.Contains(m.Name))
                              .ToArray();
                foreach (var method in methods)
                {
                    MethodUtils.DefineClassMethod(typeBuilder, method, targetField);
                }

                proxyType = typeBuilder.CreateType();
                _proxyTypes[proxyTypeName] = proxyType;
                return(proxyType);
            }
        }