2012-01-02 84 views
4

我在Reflector中查看LazyInitializer.EnsureInitialized(ref T, Func{T}),並且在該方法中似乎有一個易變的局部變量volatile object local1 = s_barrier; 。我能想到的這兩個可能的原因:LazyInitializer.EnsureInitialized中的易變局部變量?

  1. .NET可能會使用不是由給定的語言支持的功能,或

  2. 實際的代碼不聲明揮發性局部變量,但是當編譯的代碼被Reflector反編譯時,它看起來像一個易變的局部變量。

有沒有人知道這是什麼情況(或者是否有其他解釋)?如果是反編譯問題,是否有人知道「真實」代碼的外觀?

回答

2

這看起來像一個反射器錯誤:它只是s_barrier字段的正常易失性讀取。這裏沒有「特殊」的IL,這在C#中是不可表達的。

L_000d: volatile. 
L_000f: ldsfld object modreq(System.Runtime.CompilerServices.IsVolatile) System.Threading.LazyInitializer::s_barrier 

這僅僅是正常碼從靜態揮發性字段在讀取時的編譯器生成。


這裏的一個更簡單的REPRO:剛編譯以下(包裝在一個型)釋放模式:

private static volatile object field; 

private static void Main() 
{ 
    var temp = field; 
} 

反射器產生以下反編譯C#:

private static void Main() 
{ 
    volatile object field = Program.field; 
} 

時IL實際上是:

L_0000: volatile. 
L_0002: ldsfld object modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) WindowsFormsApplication1.Program::field 
L_0007: pop 
L_0008: ret 

UPDATE: 下面是我對所發生的事情的猜測:在釋放模式,C#編譯器優化離開現場(揮發性讀取的結果)的值與局部變量(stloc指令),因爲分配當地沒有被使用。這似乎混淆了反射器。如果您更改了使用隨後使用的本地方法,則確實會發射stloc(或類似的)指令,之後來自Reflector的反編譯輸出看起來很明智。

+0

同意。以某種方式特定於發佈版本。 – 2012-01-02 15:32:02

+0

我遇到了同一行代碼。你能解釋爲什麼這樣實現嗎?因爲我不明白爲什麼需要這條線 – Mark 2016-08-19 15:59:21

1

Ani是對的。這是實際的源代碼,從Reference Source中檢索。反射器是很好的,但不能堅持實際評論的源代碼蠟燭。

public static T EnsureInitialized<T>(ref T target) where T : class 
    { 
     // Fast path. 
     if (target != null) 
     { 
      object barrierGarbage = s_barrier; // Insert a volatile load barrier. Needed on IA64. 

      return target; 
     } 

     return EnsureInitializedCore<T>(ref target, LazyHelpers<T>.s_activatorFactorySelector); 
    } 

命名選擇也提供了一些有關微軟程序員關於爲鈦編寫代碼的樂趣的想法。

+0

+1,總是看到最初的源代碼。 – Ani 2012-01-02 15:46:42

+0

@Hans,也謝謝你。我設法在不知道實際評論的源代碼可用的情況下一直這樣做。 – 2012-01-02 17:17:35

+0

這可能是安騰? ;) – Slugart 2012-07-19 13:30:09