反射Emit中的TypeBuilder.DefineMethod并没有直接提供对泛型参数的支持。整体过程也不是一个简单的方法调用就可以解决的,具体总结如下过程:
- 调用没有指定参数和返回值类型的TypeBuilder.DefineMethod重载,得到MethodBuilder。
- 使用MethodBuilder.DefineGenericParameters方法来定义泛型参数的类型名称,比如T,K,V……然后最终方法就是xxx<T, K, V>。
- 使用GenericTypeParameterBuilder来添加泛型约束。
- 用MethodBuilder的SetParameters或者SetReturnType或者SetSignature定义方法的返回值和参数类型。这里可以加入泛型类型:GenericTypeParameterBuilder或者非泛型类型Type。由于GenericTypeParameterBuilder也是继承与Type的。
- 用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下打开方法: