2012-07-06 62 views
6

说我有一个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(20,10)是否会物理覆盖之前由foos [1]保存的相同内存位置?

+1

据我记得,是的。结构是值类型,所以不是覆盖foos [i]的引用(就像你看到的类),而是覆盖整个结构在数组中的那个位置。垃圾收集器不必清理结构,因为它被物理覆盖。为了测试,当Foo被声明为'struct'并且当它被声明为'class'时,计算Foo数组的大小,理论上,大小应该是'nElements * IntPtr.Size'类和nElements * sizeof(Foo)'为结构体;但我从来没有在C#中试过,所以我可能是错的。 – 2012-07-06 02:11:09

+0

我希望你能以某种方式使它成为'new'直接覆盖Foo对象,而不是创建并复制。 – SimpleVar 2012-07-06 02:13:14

+0

@Yorye我认为它会和声明long i = 1 + 2一样;值3从表达式(1 + 2)创建,然后复制到i中。所以真的,它与C风格语言通常的表现没有什么不同。 – CodeFusionMobile 2012-07-06 02:18:33

回答

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 
总之

所以,代码加载在寄存器中的常数,然后这些寄存器与的相关部分相关联的存储器的值复制数组实例。

1

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

+0

所以表达式(new Foo(20,10))的计算结果为该值,然后将其复制到位置foos [1]? – CodeFusionMobile 2012-07-06 02:09:31

+0

是的。 Brian Rasmussen的回答显示了深层内部发生的事情。 – 2012-07-06 04:05:21

0

创建一个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),则这种差异可能很重要。

相关问题