MSIL 中的 [opt] 是什么意思?

发布于 2024-10-29 01:01:55 字数 1577 浏览 1 评论 0原文

我发现 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) Name : a flags: [可选] [ HasDefault] (00001010) Default: (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

方法主体的第二行调用我的自定义属性的构造函数。

所以这引起了我的疑问:

  1. [opt]是什么意思?我的意思是出现在方法 A 和 B 的参数前面的那个。
  2. 为什么方法 C 调用应用于其参数的属性的构造函数,而方法 A 和 B 却没有调用?
  3. 我似乎在元数据中找不到 DefaultParameterValueAttribute 的任何标志,但我可以找到OptionalAttribute 和 MyTestAttribute。这是为什么?我有什么遗漏的吗?

提前致谢。

I found the "optional parameters" feature in C# 4.0 very interesting, so I tried to figure out how they made it happen.
so I wrote a method like this:

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

Compiled it, then decompiled it in IL DASM, this is the IL code:

.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

And it has got this in its metadata:

(1) ParamToken : (08000002) Name : a flags: [Optional] [HasDefault] (00001010) Default: (I4) 5

So I followed the clue and wrote a method like this:

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

Compiled it and decompiled it, and I found that the C# compiler generated almost the identical MSIL code for method A and B(except for the name).

As we can see there is no sign of attributes in the IL code and it felt wrong, so I wrote a custom attribute like this:

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

Then used it in method C like this:

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

Compiled it and then decompiled it, and hah, I found this:

.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

The second line of the method body calls to the ctor of my custom attribute.

So this leads to my doubts:

  1. What does [opt] mean? I mean the one that appears in front of method A and B's parameter.
  2. How come method C calls the constructor of the Attribute that is applied to its parameter and method A and B do not?
  3. I can not seem to find any sign of DefaultParameterValueAttribute in the metadata, but I can find OptionalAttribute and MyTestAttribute. Why is that? Is there something that I am missing?

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

っ〆星空下的拥抱 2024-11-05 01:01:55

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)。具有默认值的参数将在常量元数据表中具有关联的标记。

Constant 元数据表有一个 Parent 列(它是相关的 Param 标记)和一个 Value 列(它是存储默认值的 Blob 堆的索引)。

因此,回答您的问题:

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

The C# compiler doesn't need to emit the attributes since the Param metadata table can already describe optional and default values via the Flags column.

From 23.1.13 in ECMA 335:

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  

A parameter can have a flag value that specifies it is optional and has a default value (0x0010 | 0x1000). Parameters that have a default value will have an associated token in the Constant metadata table.

The Constant metadata table has a Parent column that would be the Param token in question and a Value column that would be an index into the blob heap where the default value is stored.

So to answer your questions:

  1. [opt] means the Flags column for the Param token has the Optional flag set.
  2. As I stated above, my guess here is that the C# compiler is recognizing the Optional/DefaultParameterValue attributes and simply converting them to parameter flags.
  3. Edit: It appears that the C# compiler is emitting an unused TypeRef for OptionalAttribute, despite the Optional flag being used for the parameter. It doesn't emit a TypeRef for DefaultParameterValueAttribute, though. It could be a small compiler bug for emitting unused TypeRefs/MemberRefs.
傾城如夢未必闌珊 2024-11-05 01:01:55

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


关于[Serialized](评论)的那个点;这是一个示例:

[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

2 / 3; there are a few attributes that the compiler interprets as IL metadata, not really attributes; it looks like this is the case here; [Serializable] is another example. The data for the default is there: Default: (I4) 5 - not all attributes in code become attributes in the metadata (again, I'm looking at [Serializable] here)


re that point on [Serializable] (comments); here's an example:

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

[Serializable]
class Bar { }

for which the core IL is:

.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
    {
    }

}

In Foo (for some arbitrary attribute), we get:

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

However this does not apply for [Serializable]; instead, that is part of the type:

.class private auto ansi serializable beforefieldinit Bar
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文