反射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下開啟方法: