【问题标题】:Using TypeBuilder to create a pass-through constructor for the base class使用 TypeBuilder 为基类创建传递构造函数
【发布时间】:2011-07-29 21:35:46
【问题描述】:

假设我有一个SpaceShip 类,如下所示:

public class SpaceShip {
    public SpaceShip() {  }
    public SpaceShip(IRocketFuelSource fuelSource) {  }
}

我想使用TypeBuilder 在运行时创建一个继承自 SpaceShip 的类型,并为 SpaceShip 中的每个定义一个构造函数。除了将它们的参数传递给父级(“传递”构造函数)之外,我不需要构造函数来实际任何事情。例如,如果用 C# 表示,生成的类型将如下所示:

public class SpaceShipSubClass : SpaceShip {
    public SpaceShipSubClass() : base() {  }
    public SpaceShipSubClass(IRocketFuelSource fuelSource) : base(fuelSource) {  }
}

为了让事情复杂一点,在运行时之前我实际上并不知道生成的类型将从哪个类继承(所以我必须考虑任意数量的构造函数,可能带有默认参数)。

这可能吗?如果我有一个大致的方向,我想我可以弄清楚,只是我对TypeBuilder 完全陌生。

谢谢!

【问题讨论】:

    标签: c# reflection typebuilder


    【解决方案1】:

    好的,我在网上找不到任何东西,所以我最终实现了自己的。这也应该有助于开始编写某种代理的任何人。

    public static class TypeBuilderHelper
    {
        /// <summary>Creates one constructor for each public constructor in the base class. Each constructor simply
        /// forwards its arguments to the base constructor, and matches the base constructor's signature.
        /// Supports optional values, and custom attributes on constructors and parameters.
        /// Does not support n-ary (variadic) constructors</summary>
        public static void CreatePassThroughConstructors(this TypeBuilder builder, Type baseType)
        {
            foreach (var constructor in baseType.GetConstructors()) {
                var parameters = constructor.GetParameters();
                if (parameters.Length > 0 && parameters.Last().IsDefined(typeof(ParamArrayAttribute), false)) {
                    //throw new InvalidOperationException("Variadic constructors are not supported");
                    continue;
                }
    
                var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();
                var requiredCustomModifiers = parameters.Select(p => p.GetRequiredCustomModifiers()).ToArray();
                var optionalCustomModifiers = parameters.Select(p => p.GetOptionalCustomModifiers()).ToArray();
    
                var ctor = builder.DefineConstructor(MethodAttributes.Public, constructor.CallingConvention, parameterTypes, requiredCustomModifiers, optionalCustomModifiers);
                for (var i = 0; i < parameters.Length; ++i) {
                    var parameter = parameters[i];
                    var parameterBuilder = ctor.DefineParameter(i + 1, parameter.Attributes, parameter.Name);
                    if (((int)parameter.Attributes & (int)ParameterAttributes.HasDefault) != 0) {
                        parameterBuilder.SetConstant(parameter.RawDefaultValue);
                    }
    
                    foreach (var attribute in BuildCustomAttributes(parameter.GetCustomAttributesData())) {
                        parameterBuilder.SetCustomAttribute(attribute);
                    }
                }
    
                foreach (var attribute in BuildCustomAttributes(constructor.GetCustomAttributesData())) {
                    ctor.SetCustomAttribute(attribute);
                }
    
                var emitter = ctor.GetILGenerator();
                emitter.Emit(OpCodes.Nop);
    
                // Load `this` and call base constructor with arguments
                emitter.Emit(OpCodes.Ldarg_0);
                for (var i = 1; i <= parameters.Length; ++i) {
                    emitter.Emit(OpCodes.Ldarg, i);
                }
                emitter.Emit(OpCodes.Call, constructor);
    
                emitter.Emit(OpCodes.Ret);
            }
        }
    
    
        private static CustomAttributeBuilder[] BuildCustomAttributes(IEnumerable<CustomAttributeData> customAttributes)
        {
            return customAttributes.Select(attribute => {
                var attributeArgs = attribute.ConstructorArguments.Select(a => a.Value).ToArray();
                var namedPropertyInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType<PropertyInfo>().ToArray();
                var namedPropertyValues = attribute.NamedArguments.Where(a => a.MemberInfo is PropertyInfo).Select(a => a.TypedValue.Value).ToArray();
                var namedFieldInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType<FieldInfo>().ToArray();
                var namedFieldValues = attribute.NamedArguments.Where(a => a.MemberInfo is FieldInfo).Select(a => a.TypedValue.Value).ToArray();
                return new CustomAttributeBuilder(attribute.Constructor, attributeArgs, namedPropertyInfos, namedPropertyValues, namedFieldInfos, namedFieldValues);
            }).ToArray();
        }
    }
    

    用法(假设您有一个TypeBuilder 对象——例如,请参阅here):

    var typeBuilder = ...;  // TypeBuilder for a SpaceShipSubClass
    typeBuilder.CreatePassThroughConstructors(typeof(SpaceShip));
    var subType = typeBuilder.CreateType();  // Woo-hoo, proxy constructors!
    

    【讨论】:

    • 正是我想要的!虽然我不得不更改为GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) 来获得那些讨厌的protected 构造函数..
    • @Dan:太好了,我不确定其他人是否需要这个。感谢您添加该关键字,我必须(错误)手动编写声明,然后仅复制粘贴正文。
    • 这位先生,是纯金的!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2021-08-13
    • 2013-05-11
    • 2014-12-25
    相关资源
    最近更新 更多