在C#中为数组分配新结构时会发生什么?

假设我有一个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]持有的相同内存位置?

实际上,与相关arrays槽相关联的存储器由值填充。 鉴于您的代码,一个小例子显示了发生了什么。 请在线查看评论。 这是一个发布版本。

 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 

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

foos[1]将包含new Foo(20, 10);逐位副本new Foo(20, 10); foos[1] new Foo(20, 10);

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

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