现在的位置: 首页 > 综合 > 正文

.NET(C#):反射Emit创建泛型方法(包括泛型参数约束)

2013年02月26日 ⁄ 综合 ⁄ 共 2394字 ⁄ 字号 评论关闭

反射Emit中的TypeBuilder.DefineMethod并没有直接提供对泛型参数的支持。整体过程也不是一个简单的方法调用就可以解决的,具体总结如下过程:

  1. 调用没有指定参数和返回值类型的TypeBuilder.DefineMethod重载,得到MethodBuilder。
  2. 使用MethodBuilder.DefineGenericParameters方法来定义泛型参数的类型名称,比如T,K,V……然后最终方法就是xxx<T, K, V>。
  3. 使用GenericTypeParameterBuilder来添加泛型约束。
  4. 用MethodBuilder的SetParameters或者SetReturnType或者SetSignature定义方法的返回值和参数类型。这里可以加入泛型类型:GenericTypeParameterBuilder或者非泛型类型Type。由于GenericTypeParameterBuilder也是继承与Type的。
  5. 用ILGenerator生成方法IL内容。整个方法定义结束。

 

有必要再次强调就是GenericTypeParameterBuilder也是继承与Type的,所以调用MethodBuilder的SetParameters或者SetReturnType或者SetSignature方法时传入的参数都是Type类型,此时可以传入Type或者GenericTypeParameterBuilder来代表非泛型类型和泛型类型。

 

最后就是GenericTypeParameterBuilder类型中的方法可以定义泛型参数的约束。SetBaseTypeConstraint可以限制基类。SetInterfaceConstants限制执行的借口类型(可以有多个)。SetGenericParameterAttributes定义其他属性(通过GenericParameterAttributes枚举)。比如DefaultConstructorConstraint代表可调用的无参数认构造函数(相当于:new()约束),还有ReferenceTypeConstraint和NotNullableValueTypeConstraint代表class和struct约束!(GenericParameterAttributes枚举还有其他值,这里就不一一讲述了,读者参考MSDN)。

 

注意:

 

定义泛型方法只支持在MethodBuilder类型中,DynamicMethod类型不支持。

 

让我们用反射Emit动态定义一个这样的方法:

static T doo<T, K>(T a, int b, K c, double d)

    where T : class

    where K : ICloneable, new()

{

    return a;

}

 

代码:

//+ using System.Reflection

//+ using System.Reflection.Emit

static void CreateMethod(TypeBuilder tb)

{

    //使用TypeBuilder.DefineMethod没有定义参数和返回值类型的重载

    var mbuilder = tb.DefineMethod("doo",

        MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static);

 

    //返回:GenericTypeParameterBuilder[]

    var gpas = mbuilder.DefineGenericParameters("T", "K");

 

    //设置T的约束

    gpas[0].SetGenericParameterAttributes(GenericParameterAttributes.ReferenceTypeConstraint);

    //设置K的约束

    gpas[1].SetGenericParameterAttributes(GenericParameterAttributes.DefaultConstructorConstraint);

    gpas[1].SetInterfaceConstraints(typeof(ICloneable));

 

    //定义参数类型:(T a, int b, K c, double d)

    mbuilder.SetParameters(gpas[0], typeof(int), gpas[1], typeof(double));

    //定义返回值类型:T

    mbuilder.SetReturnType(gpas[0]);

 

    //设置名称(这一步不是必须,因为没有参数名称也可以执行,IL内使用参数位置来引用参数的)

    //注意索引值从1开始,0代表返回值类型。

    mbuilder.DefineParameter(1, ParameterAttributes.None, "a");

    mbuilder.DefineParameter(2, ParameterAttributes.None, "b");

    mbuilder.DefineParameter(3, ParameterAttributes.None, "c");

    mbuilder.DefineParameter(4, ParameterAttributes.None, "d");

 

    //定义方法IL

    var ilgen = mbuilder.GetILGenerator();

    //return a;

    ilgen.Emit(OpCodes.Ldarg_0);

    ilgen.Emit(OpCodes.Ret);

}

 

动态生成程序集后在Reflector下打开方法:

image

抱歉!评论已关闭.