2011-03-31 95 views
9

我找到了「可選參數」功能,在C#4.0很有意思,所以我試圖找出他們如何使之成爲現實。 所以我寫了這樣的方法:MSIL中的[opt]是什麼意思?

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)名稱:一個標誌:[可選] [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. 是什麼[選擇]是什麼意思?我的意思是出現在方法A和B的參數前面的那個。
  2. 怎麼來的方法C調用被應用到它的參數和方法A和B沒有屬性的構造函數?
  3. 我似乎無法找到在元數據DefaultParameterValueAttribute的跡象,但我能找到OptionalAttribute和MyTestAttribute。這是爲什麼?有什麼我失蹤了嗎?

在此先感謝。

回答

10

C#編譯不需要發射的屬性,因爲帕拉姆元數據表可以已經通過Flags列描述可選的和缺省值。

從13年1月23日在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 

的參數可以指定它是可選的,默認值的標誌值(0×0010 | 0×1000)。具有默認值的參數將在常量元數據表中具有關聯的標記。

常量元數據表有一個Parent列,該列將成爲問題中的Param標記,而Value列將成爲存儲默認值的blob堆中的索引。

因此,要回答你的問題:

  1. [選擇]表示對帕拉姆令牌Flags列具有可選的標誌設置。
  2. 如上所述,我的猜測是C#編譯器正在識別Optional/DefaultParameterValue屬性,並將它們簡單地轉換爲參數標誌。
  3. 編輯:儘管參數使用了可選標誌,但似乎C#編譯器正在爲OptionalAttribute發出未使用的TypeRef。不過,它不會爲DefaultParameterValueAttribute發出TypeRef。它可能是一個用於發射未使用的TypeRefs/MemberRefs的小編譯器錯誤。
+0

謝謝,我沒想到能儘快得到答案。但你的看起來像那個。 – CuiPengFei 2011-03-31 09:42:34

+0

而我一直在考慮優化意味着優化... – CuiPengFei 2011-03-31 09:50:46

+0

哇,沒想到找到了一個CSC的bug。 – CuiPengFei 2011-04-01 08:06:03

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 
+1

謝謝,這解釋了很多。順便說一句,當我搜索「可選參數」時,我看到了你的帖子,:) – CuiPengFei 2011-03-31 09:31:31

+0

我剛剛測試過,實際上,Serializable仍然出現在元數據的reftype部分。 – CuiPengFei 2011-04-01 06:30:40

+0

@CuiPengFei - 更新來說明 – 2011-04-01 06:35:25