参考TypeBuilder.DefineGenericParameters Method,有很完整的代码和描述。
这个例子中做的事情是:
-
定义一个动态程序集(DynamicAssembly),
// Define a dynamic assembly to contain the sample type. The // assembly will not be run, but only saved to disk, so // AssemblyBuilderAccess.Save is specified. // AppDomain myDomain = AppDomain.CurrentDomain; AssemblyName myAsmName = new AssemblyName("GenericEmitExample1"); AssemblyBuilder myAssembly = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
-
动态程序集由多个可执行模块组成。对于只有一个可执行模块的程序集,模块名称应该和程序集名称一致。
// An assembly is made up of executable modules. For a single- // module assembly, the module name and file name are the same // as the assembly name. // ModuleBuilder myModule = myAssembly.DefineDynamicModule(myAsmName.Name, myAsmName.Name + ".dll");
需要用到GenericTypeParameterBuilder 。
最终得到的MSIL代码是:
.method public static class [mscorlib]System.Collections.Generic.List`1<!TFirst>
ExampleMethod(!TFirst[] A_0) cil managed
{
// 代码大小 7 (0x7)
.maxstack 2
IL_0000: ldarg.0
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<!TFirst>::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<!0>)
IL_0006: ret
} // end of method Sample::ExampleMethod
其中!0
的解释参考Why is !0 a type in Microsoft Intermediate Language (MSIL)?,You need to read !n as the n-th type argument of the generic type. Where !0 means "first type argument", !1 means "second type argument", etcetera. For Nullable<>, you know that '!0` means 'T' from the MSDN article。
如果直接写C#类和函数:
public class MyClass
{
public static List<TFirst> ExampleMethod<TFirst>(TFirst[] A_0)
{
return new List<TFirst>(A_0);
}
}
得到的MSIL代码是:
.method public hidebysig static class [mscorlib]System.Collections.Generic.List`1<!!T>
ExampleMethod<T>(!!T[]& A_0) cil managed
{
// 代码大小 13 (0xd)
.maxstack 1
.locals init ([0] class [mscorlib]System.Collections.Generic.List`1<!!T> V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldind.ref
IL_0003: newobj instance void class [mscorlib]System.Collections.Generic.List`1<!!T>::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<!0>)
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
} // end of method MyClass::ExampleMethod
注意前一个代码中是“!TFirst”,这里是“!!T”.两个”!!“(Two exclamation marks),indicate a type argument for a generic method.
将MyClass
重新定义如下:
public class MyClass<TFirst>
{
public static List<TFirst> ExampleMethod(TFirst[] A_0)
{
return new List<TFirst>(A_0);
}
}
得到的MSIL代码就和IL动态生成的代码基本一致了(没有两个!!):
.method public hidebysig static class [mscorlib]System.Collections.Generic.List`1<!TFirst>
ExampleMethod(!TFirst[] A_0) cil managed
{
// 代码大小 12 (0xc)
.maxstack 1
.locals init ([0] class [mscorlib]System.Collections.Generic.List`1<!TFirst> V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: newobj instance void class [mscorlib]System.Collections.Generic.List`1<!TFirst>::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<!0>)
IL_0007: stloc.0
IL_0008: br.s IL_000a
IL_000a: ldloc.0
IL_000b: ret
} // end of method MyClass`1::ExampleMethod
也就是说,如果是模板类,选择类型的时候就是一个叹号。如果是模板函数,使用两个叹号。
多出来的几句IL_0007、IL_0008、IL_000a,”br.s“表示“Unconditionally transfers control to a target instruction (short form).” 这几句代码是为了调试时使用的,参考Why is the 'br.s' IL opcode used in this case?。所以换成Release模式编译,就得到了这样的MSIL代码:
.method public hidebysig static class [mscorlib]System.Collections.Generic.List`1<!TFirst>
ExampleMethod(!TFirst[] A_0) cil managed
{
// 代码大小 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<!TFirst>::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<!0>)
IL_0006: ret
} // end of method MyClass`1::ExampleMethod
这就与IL动态生成代码基本一致了,除了“hidebysig”,它是表示“hide-by-signature”,即如果子类中方法签名与父类方法完全一致,才会覆盖父类方法。这区别月“hide-by-name”。如何在C#中编写代码不使用hidebysig呢?尚未得知……