2009-09-09 25 views
11

任何人都知道爲什麼將T約束到類的泛型方法會生成MSIL代碼中的裝箱指令?爲什麼具有T:class的constants的泛型方法會導致裝箱?

我對此非常驚訝,因爲T被限制爲引用類型,生成的代碼不需要執行任何裝箱。

這裏是C#代碼:

protected void SetRefProperty<T>(ref T propertyBackingField, T newValue) where T : class 
{ 
    bool isDifferent = false; 

    // for reference types, we use a simple reference equality check to determine 
    // whether the values are 'equal'. We do not use an equality comparer as these are often 
    // unreliable indicators of equality, AND because value equivalence does NOT indicate 
    // that we should share a reference type since it may be a mutable. 

    if (propertyBackingField != newValue) 
    { 
     isDifferent = true; 
    } 
} 

這裏是產生IL:

.method family hidebysig instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed 
{ 
    .maxstack 2 
    .locals init (
     [0] bool isDifferent, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: ldarg.1 
    L_0004: ldobj !!T 
    L_0009: box !!T 
    L_000e: ldarg.2 
    L_000f: box !!T 
    L_0014: ceq 
    L_0016: stloc.1 
    L_0017: ldloc.1 
    L_0018: brtrue.s L_001e 
    L_001a: nop 
    L_001b: ldc.i4.1 
    L_001c: stloc.0 
    L_001d: nop 
    L_001e: ret 
} 

通知的框!!Ť指令。

任何任何想法,爲什麼這是正在生成?

任何任何想法如何避免這種情況?

感謝, 菲爾

+1

喬恩不在我猜:-) – Peter 2009-09-09 15:44:17

+7

我找到了你的答案,它是一個重複的!順便問一下:)請參閱http://stackoverflow.com/questions/646517/boxing-when-using-generics-in-c – 2009-09-09 16:15:00

+3

我已經鏈接的答案的要點是,一個參考拳擊指令類型實際上是一個nop。這允許編譯器自由地發出可以被JIT移除的裝箱指令,該指令用於使用作爲泛型類型參數的引用類型創建的封閉構造類型。在你的情況下(因爲'T'被限制爲一個引用類型)所發出的兩個裝箱指令都不會被運行。 – 2009-09-09 16:19:26

回答

2

您不必擔心box指令的任何性能下降,因爲如果它的參數是引用類型,則box指令不執行任何操作。雖然指令box甚至已經創建(可能是代碼生成中的懶/易設計?)仍然很奇怪。

0

我相信這是設計意圖。你並沒有將T約束到一個特定的類,所以最有可能將它轉換爲對象。因此你爲什麼看到IL包括拳擊。

我會嘗試這個代碼,其中T:ActualClass

+3

如果你這樣做T:ActualClass,爲什麼要打擾泛型? – 2009-09-09 16:06:56

+0

因爲你可以將T約束到更高的層次......比如iSomeInterface ... – 2009-09-09 18:33:56

+1

Chris,如果T是一個對象,在推入堆棧之前是不是已經裝好了盒子?那麼爲什麼拳擊手術需要進行呢? 我期望==運算符檢查引用相等如果T是一個對象,所以這也不需要un/boxing操作。 – Phil 2009-09-09 19:52:53

1

我不知道爲什麼任何拳ocurring。避免拳擊的一種可能的方式是不使用它。只需重新編譯沒有拳擊。例如:

.assembly recomp_srp 
{ 
    .ver 1:0:0:0 
} 

.class public auto ansi FixedPBF 
{ 

.method public instance void .ctor() cil managed 
{ 

} 

.method hidebysig public instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed 
{ 
    .maxstack 2  
     .locals init (bool isDifferent, bool CS$4$0000) 

     ldc.i4.0 
     stloc.0 
     ldarg.1 
     ldobj !!T 
     ldarg.2 
     ceq 
     stloc.1 
     ldloc.1 
     brtrue.s L_0001 
     ldc.i4.1 
     stloc.0 
     L_0001: ret 

} 

} 

...如果你保存到recomp_srp.msil你一個文件可以簡單地重新編譯爲這樣:

反彙編/ DLL recomp_srp.msil

,不受拳擊上運行正常我的目的:

 FixedPBF TestFixedPBF = new FixedPBF(); 

     TestFixedPBF.SetRefProperty<string>(ref TestField, "test2"); 

...當然,我改變了它從保護公衆,你需要做出改變回來,並提供您的實現的其餘部分。

相關問題