.Net IL语言如何定义带参数的泛型类型

参考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 
            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::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1)
      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 ExampleMethod(TFirst[] A_0)
        {
            return new List(A_0);
        }
    }

得到的MSIL代码是:

    .method public hidebysig static class [mscorlib]System.Collections.Generic.List`1 
            ExampleMethod(!!T[]& A_0) cil managed
    {
      // 代码大小       13 (0xd)
      .maxstack  1
      .locals init ([0] class [mscorlib]System.Collections.Generic.List`1 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::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1)
      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
    {
        public static List ExampleMethod(TFirst[] A_0)
        {
            return new List(A_0);
        }
    }

得到的MSIL代码就和IL动态生成的代码基本一致了(没有两个!!):

    .method public hidebysig static class [mscorlib]System.Collections.Generic.List`1 
            ExampleMethod(!TFirst[] A_0) cil managed
    {
      // 代码大小       12 (0xc)
      .maxstack  1
      .locals init ([0] class [mscorlib]System.Collections.Generic.List`1 V_0)
      IL_0000:  nop
      IL_0001:  ldarg.0
      IL_0002:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1)
      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 
            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::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1)
      IL_0006:  ret
    } // end of method MyClass`1::ExampleMethod

这就与IL动态生成代码基本一致了,除了“hidebysig”,它是表示“hide-by-signature”,即如果子类中方法签名与父类方法完全一致,才会覆盖父类方法。这区别月“hide-by-name”。如何在C#中编写代码不使用hidebysig呢?尚未得知……

你可能感兴趣的:(.Net IL语言如何定义带参数的泛型类型)