使用TypeBuilder为基类创建传递构造函数

Cam*_*ron 16 c# reflection typebuilder

说我有一个SpaceShip班级,如下:

public class SpaceShip {
    public SpaceShip() {  }
    public SpaceShip(IRocketFuelSource fuelSource) {  }
}
Run Code Online (Sandbox Code Playgroud)

我想用于TypeBuilder在运行时创建一个继承自SpaceShip的类型,并为SpaceShip中的每个类型定义一个构造函数.我不需要构造函数实际任何事情,除了将它们的参数传递给父("传递"构造函数).例如,如果用C#表示,生成的类型看起来像这样:

public class SpaceShipSubClass : SpaceShip {
    public SpaceShipSubClass() : base() {  }
    public SpaceShipSubClass(IRocketFuelSource fuelSource) : base(fuelSource) {  }
}
Run Code Online (Sandbox Code Playgroud)

为了使事情复杂化,我实际上并不知道生成类型将继承到哪个类直到运行时(因此我将不得不考虑任意数量的构造函数,可能使用默认参数).

这可能吗?如果我有一个大方向开始,我想我可以弄明白,只是我完全是新手TypeBuilder.

谢谢!

Cam*_*ron 25

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

用法(假设你有一个TypeBuilder对象 - 参见这里的例子):

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

  • 正是我在寻找的!虽然我不得不改为`GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)`来获取那些讨厌的`protected`构造函数. (2认同)