2010-10-29 85 views
0

我有一个具有泛型类型的私有变量的类。
在类中我已经声明了一个Foo方法。
检查IL后,我发现方法Push的目标实际上是方法调用set_Property2,而不是类的字段。
编译器如何在两者之间建立连接?有关字段参考的MSIL问题

 

public class A 
{ 
    public int Property1 { get; set; } 
    public int Property2 { get; set; } 
} 

public class ShortDemo 
{ 
    private Stack<A> _stack = new Stack<A>(); 

    private void Foo() 
    { 
     _stack.Push(new A() 
     { 
      Property1 = 1, 
      Property2 = 2 
     }); 
    } 
} 
 

而IL:

 

.method private hidebysig instance void Foo() cil managed 
{ 
    .maxstack 3 
    .locals init (
     [0] class ConsoleApplication1.A g__initLocal0) 
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldfld class [System]System.Collections.Generic.Stack1 ConsoleApplication1.ShortDemo::_stack 
    L_0007: newobj instance void ConsoleApplication1.A::.ctor() 
    L_000c: stloc.0 
    L_000d: ldloc.0 
    L_000e: ldc.i4.1 
    L_000f: callvirt instance void ConsoleApplication1.A::set_Property1(int32) 
    L_0014: nop 
    L_0015: ldloc.0 
    L_0016: ldc.i4.2 
    L_0017: callvirt instance void ConsoleApplication1.A::set_Property2(int32) 
    L_001c: nop 
    L_001d: ldloc.0 
    L_001e: callvirt instance void [System]System.Collections.Generic.Stack1::Push(!0) 
    L_0023: nop 
    L_0024: ret 
} 
 
+0

您的字段在L_0024加载,然后将方法的参数放在堆栈上,然后在L_0052处调用Push。问题是什么? – kvb 2010-10-29 17:12:28

+0

这里有太多的事情要做,以便于分析。请提供一个简短但完整的程序来证明这一点,尽可能使用简短的方法。 – 2010-11-05 15:54:27

+0

我注意到它仅在使用对象初始值设定项时发生。当我创建A的新实例并将其推入堆栈时,ldfld行将从L_0002中省略并插入L_001a,这对我来说很有意义。 – Sagi 2010-11-05 16:37:39

回答

2

我没有看到一个问题。下面是选项:

A a = new A() { ... }; 
_stack.Push(args) 

翻译为:

  • 创建对象,并在当地的0
  • 负载_STACK场
  • 加载本地0
  • 呼叫推
存储参考

现在你的“嵌入式”对象初始值vers离子:

_stack.Push(new A() { ... }); 

翻译为:

  • 负载_STACK场
  • 创建本地0
  • 负载对象和存储本地0
  • 呼叫推

在这两个情况下,堆栈结束了字段和参数。只是在“嵌入式​​”对象初始化版本中,加载字段和调用方法之间还有更多的事情要做。

0

根据反射器

L_0001:“此”参考位于顶部。
L_0002:从堆栈弹出值后,字段引用位于顶部。
L_0007:新的ConsoleApplication1.A对象位于顶部。
L_000c:从堆栈中弹出值后,分配表达式位于顶部。

(所以现在我们有两个表达式分配在顶部和字段引用)。

L_000d:在顶部的可变参考。
L_000e:文字顶部的int值。
L_000f:set_Property1方法调用在弹出文字值后用作
参数和变量引用作为目标。
(现在我们有3个表达式方法调用set_Property1,分配表达式然后字段
参考)。

L_0017:与L_000f相同,结束于方法调用堆栈 (set_Property2,set_Property1,assign,field reference)。
L_001d:varaible参考 (可变参考,set_Property2,set_Property1 ...)

L_001e:推送方法需要一个目标和一个参数。如果我假设这条线与L_000f线相同,那么位于堆栈顶部的目标是set_Property2。

我不明白你为什么写了堆栈结束字段和参数。

+0

这不是一个真正的答案。作为一个问题编辑会更好。 – 2010-11-11 22:19:36