2012-07-06 64 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),則這種差異可能很重要。

相關問題