2012-01-02 78 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