如果有人能告诉我我是否了解我,我将不胜感激:
class X { A a1=new A(); // reference on the stack, object value on the heap a1.VarA=5; // on the stack - value type A a2=a1; // reference on the stack, object value on the heap a2.VarA=10; // on the stack - value type }
而且a1和a2引用都在堆栈上,而它们的“对象”值在堆上。但是VarA变量,它仍然是纯值类型呢?
a1
a2
VarA
class A { int VarA; }
您在询问有关 实现细节的 问题,因此答案将取决于特定的实现。让我们考虑一下实际编译的程序版本:
class A { public int VarA; } class X { static void Main(string[] args) { A a1 = new A(); a1.VarA = 5; A a2 = a1; a2.VarA = 10; } }
这是在调试模式下运行C#4.0的Microsoft CLR 4.0上发生的情况。
此时,堆栈帧指针已复制到寄存器ebp中:
在这里,我们为新对象分配堆内存。
A a1 = new A(); mov ecx,382518h call FFE6FD30
这将返回对eax中堆对象的引用。我们将引用存储在堆栈插槽ebp-48中,这是一个与任何名称都没有关联的临时插槽。请记住,a1尚未初始化。
mov dword ptr [ebp-48h],eax
现在,我们使用刚刚存储在堆栈中的引用,并将其复制到ecx中,它将用作指向ctor调用的“ this”指针。
mov ecx,dword ptr [ebp-48h]
现在我们称为ctor。
call FFE8A518
现在,我们将存储在临时堆栈插槽中的引用再次复制到寄存器eax中。
mov eax,dword ptr [ebp-48h]
现在,我们将eax中的引用复制到堆栈插槽ebp-40(即a1)中。
mov dword ptr [ebp-40h],eax
现在我们必须将a1提取到eax中:
a1.VarA = 5; mov eax,dword ptr [ebp-40h]
记住,eax现在是a1所引用的事物的堆分配数据的地址。那东西的VarA字段是对象中的四个字节,因此我们在其中存储5个字节:
mov dword ptr [eax+4],5
现在,我们将a1的堆栈插槽中的引用复制到eax,然后将其复制到a2的堆栈插槽中,即ebp-44。
A a2 = a1; mov eax,dword ptr [ebp-40h] mov dword ptr [ebp-44h],eax
现在,正如您再次期望的那样,我们将a2放入eax中,然后将参考引用四个字节,以将0x0A写入VarA:
a2.VarA = 10; mov eax,dword ptr [ebp-44h] mov dword ptr [eax+4],0Ah
因此,对于您的问题的答案是,对对象的引用存储在堆栈中的三个位置:ebp-44,ebp-48和ebp-40。它们存储在eax和ecx中的寄存器中。对象的内存(包括其字段)存储在托管堆中。这一切都在Microsoft CLR v4.0的调试版本的x86上完成。如果您想知道东西如何在其他配置中存储在堆栈,堆和寄存器中,则可能完全不同。引用可以全部存储在堆中,也可以全部存储在寄存器中。可能根本没有堆栈。这完全取决于jit编译器的作者决定实现IL语义的方式。