6

假设我有一个 C# 结构:

struct Foo{
    int mA;
    public int A {get {return mA;}}
    int mB;
    public int B {get {return mB;}}

    public Foo(int a, int b)
    {
        mA = a;
        mB = b;
    }
}

然后我创建 Foo 的数组:

Foo[] foos = new Foo[10];

当我这样做时会发生什么?

foos[1] = new Foo(20, 10);

如果 Foo 是一个类,则 Foo[] 将持有一个指向堆上 Foo 对象的指针,并且该指针将更改为新的 Foo 对象(旧对象留待回收)。

但是由于结构是值类型,新的 Foo(20, 10) 会物理覆盖之前由 foos[1] 持有的相同内存位置吗?

4

3 回答 3

5

实际上,与相关数组槽关联的内存由值填充。给定您的代码,一个小示例显示了发生的情况。请看内联评论。这是一个发布版本。

static void Main(string[] args)
{
    Foo[] foos = new Foo[10];
    foos[1] = new Foo(127, 255);
    Console.ReadLine();
}

上面的代码是JIT编译如下

// Method setup
00280050 55              push    ebp
00280051 8bec            mov     ebp,esp
00280053 56              push    esi

// Create instance of Foo[]
00280054 b98a141d00      mov     ecx,1D148Ah
00280059 ba0a000000      mov     edx,0Ah
0028005e e8b121f4ff      call    CORINFO_HELP_NEWARR_1_VC (001c2214)
00280063 8bd0            mov     edx,eax

// Array range check 
00280065 837a0401        cmp     dword ptr [edx+4],1
00280069 7624            jbe     

// Assign foos[1] = new Foo(127, 255)  
0028006b 8d4210          lea     eax,[edx+10h]  <-- load location of foos[1] in eax
0028006e ba7f000000      mov     edx,7Fh        <-- load 127 in edx
00280073 beff000000      mov     esi,0FFh       <-- load 255 in esi
00280078 8910            mov     dword ptr [eax],edx    <-- move the value 127 to foos[1]
0028007a 897004          mov     dword ptr [eax+4],esi  <-- move the value 255 to foos[1] + offset

// This is just for the Console.ReadLine() part + rest of Main
0028007d e8d2436305      call    mscorlib_ni!System.Console.get_In() (058b4454)
00280082 8bc8            mov     ecx,eax
00280084 8b01            mov     eax,dword ptr [ecx]
00280086 8b402c          mov     eax,dword ptr [eax+2Ch]
00280089 ff501c          call    dword ptr [eax+1Ch]

// Epilog
0028008c 5e              pop     esi
0028008d 5d              pop     ebp
0028008e c3              ret

//Exception handling
0028008f e8f05e7f70      call    clr!JIT_RngChkFail (70a75f84)
00280094 cc              int     3

简而言之,代码将常量加载到寄存器中,然后将这些寄存器的值复制到与数组实例的相关部分关联的内存中。

于 2012-07-06T02:45:35.517 回答
1

foos[1]将包含new Foo(20, 10);.

于 2012-07-06T02:08:30.493 回答
0

创建一个结构数组会在每个槽中创建一个默认值实例。在 C# 中,调用 C#new中的结构会创建一个新的临时实例(很可能在堆栈上)并将一个结构分配给另一个总是通过用前者中相应字段的内容覆盖后者的所有字段来改变后者的实例. 因此,该语句 使用默认值foos[1] = new Foo(20,10);创建一个新的临时实例Foo,将该实例传递给参数化构造函数,将该临时实例的所有字段复制到数组槽 1 中保存的实例,然后丢弃该临时实例。

顺便说一句,vb.net 中相应语句的行为略有不同。Sayingfoos(1) = New Foo(20,10)会将 的所有字段重置foos(1)为其默认值,然后传递foos(1)给参数化构造函数。foos(1)如果任何代码在构造函数运行时尝试访问,则这种差异可能很重要。

于 2012-07-06T14:46:06.197 回答