[opt]在MSIL中意味着什么?

时间:2011-03-31 09:09:11

标签: c# .net c#-4.0 cil il

我发现C#4.0中的“可选参数”功能非常有趣,所以我试图弄清楚它们是如何实现的。 所以我写了一个像这样的方法:

private static void A(int a = 5) { }

编译它,然后在IL DASM中反编译它,这是IL代码:

.method private hidebysig static void  A([opt] int32 a) cil managed
{
  .param [1] = int32(0x00000005)
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ret
} // end of method Program::A

它的元数据中有这个:

(1)ParamToken:(08000002)姓名:a flags:[可选] [HasDefault](00001010)默认值:(I4)5

所以我按照线索写了一个像这样的方法:

private static void B([Optional, DefaultParameterValue(78)]int b) { }

编译并反编译它,我发现C#编译器为方法A和B生成了几乎相同的MSIL代码(名称除外)。

正如我们所看到的,IL代码中没有属性的迹象,而且感觉不对,所以我写了一个这样的自定义属性:

[AttributeUsage(AttributeTargets.Parameter)]
public class MyTestAttribute : Attribute
{
}

然后在方法C中使用它,如下所示:

private static void C([MyTest]int c) { }

编译它然后反编译它,哈哈,我发现了这个:

.method private hidebysig static void  C(int32 c) cil managed
{
  .param [1]
  .custom instance void ConsoleApplication1.MyTestAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ret
} // end of method Program::C

方法体的第二行调用我的自定义属性的ctor。

所以这引起了我的怀疑:

  1. [opt]是什么意思?我的意思是出现在方法A和B的参数前面的那个。
  2. 方法C如何调用应用于其参数的Attribute的构造函数,而方法A和B不调用?
  3. 我似乎无法在元数据中找到DefaultParameterValueAttribute的任何符号,但我可以找到OptionalAttribute和MyTestAttribute。这是为什么?我有什么遗失的东西吗?
  4. 提前致谢。

2 个答案:

答案 0 :(得分:10)

C#编译器不需要发出属性,因为Param元数据表已经可以通过Flags列描述可选值和默认值。

ECMA 335的23.1.13开始:

Flag            Value   Description
-----------------------------------------------------
In              0x0001  Parameter is [In]  
Out             0x0002  Parameter is [Out]  
Optional        0x0010  Parameter is optional  
HasDefault      0x1000  Parameter has a default value  
HasFieldMarshal 0x2000  Parameter has FieldMarshal  

参数可以有一个标志值,指定它是可选的,并且具有默认值(0x0010 | 0x1000)。具有默认值的参数将在常量元数据表中具有关联的标记。

常量元数据表有一个Parent列,它将是所讨论的Param令牌,以及一个Value列,它将成为存储默认值的blob堆的索引。

所以回答你的问题:

  1. [opt]表示Param令牌的Flags列设置了可选标志。
  2. 如上所述,我的猜测是C#编译器正在识别Optional / DefaultParameterValue属性,只是将它们转换为参数标志。
  3. 编辑:看起来C#编译器正在为OptionalAttribute发出一个未使用的TypeRef,尽管可选标志用于参数。但是,它不会为DefaultParameterValueAttribute发出TypeRef。它可能是一个小的编译器错误,用于发出未使用的TypeRefs / MemberRefs。

答案 1 :(得分:2)

2/3;编译器将一些属性解释为IL元数据,而不是真正的属性;看起来这就是这种情况; [Serializable]是另一个例子。默认的数据是:Default: (I4) 5 - 并非代码中的所有属性都成为元数据中的属性(同样,我在这里看[Serializable]


重点[Serializable](评论);这是一个例子:

[Description("abc")]
class Foo { }

[Serializable]
class Bar { }

核心IL是:

.class private auto ansi beforefieldinit Foo
    extends [mscorlib]System.Object
{
    .custom instance void [System]System.ComponentModel.DescriptionAttribute::.ctor(string) = { string('abc') }
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
    }

}
.class private auto ansi serializable beforefieldinit Bar
    extends [mscorlib]System.Object
{
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
    }

}

Foo(对于某些任意属性)中,我们得到:

.custom instance void [System]System.ComponentModel.DescriptionAttribute::.ctor(string) = { string('abc') }

但这不适用于[Serializable];相反,这是类型的一部分:

.class private auto ansi serializable beforefieldinit Bar