2012-07-06 40 views
6

Nói rằng tôi có một C# struct:Điều gì sẽ xảy ra khi gán cấu trúc mới cho mảng trong 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; 
    } 
} 

Và sau đó tôi có thể tạo và mảng của Foo của:

Foo[] foos = new Foo[10]; 

xảy ra khi tôi làm gì đây?

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

Nếu Foo là một lớp học, Foo [] sẽ giữ một con trỏ đến một đối tượng Foo trên heap, và con trỏ sẽ được thay đổi để đối tượng Foo mới (cái cũ bị bỏ lại để tái chế).

Nhưng vì cấu trúc là loại giá trị, Foo mới (20, 10) chỉ ghi đè vật lý cùng một vị trí bộ nhớ trước đó được lưu giữ bởi foos [1]?

+1

Theo như tôi nhớ, có. Structs là các kiểu giá trị, vì vậy thay vì ghi đè lên tham chiếu tại foos [i] (như bạn sẽ thấy với một lớp), bạn sẽ ghi đè toàn bộ cấu trúc tại vị trí đó trong mảng. Các nhà sưu tập rác sẽ không phải làm sạch các cấu trúc như nó đã được ghi đè vật lý. Để kiểm tra, tính toán kích thước của một mảng Foo khi Foo được khai báo là 'struct' và khi nó được khai báo là một' lớp', về mặt lý thuyết, kích thước phải là 'nElements * IntPtr.Size' cho các lớp và' nElements * sizeof (Foo) 'cho cấu trúc; nhưng tôi chưa bao giờ thử điều này trong C# vì vậy tôi có thể sai. –

+0

Tôi muốn bạn bằng cách nào đó có thể làm cho nó để 'mới' thực sự sẽ ghi đè lên một đối tượng Foo trực tiếp, thay vì tạo và sau đó sao chép. – SimpleVar

+0

@Yorye Tôi nghĩ rằng nó sẽ giống như tuyên bố dài i = 1 + 2; Giá trị 3 được tạo ra từ biểu thức (1 + 2) và sau đó được sao chép vào i. Vì vậy, thực sự, nó không khác với cách ngôn ngữ kiểu C thường hoạt động. – CodeFusionMobile

Trả lời

5

Trong thực tế, bộ nhớ được kết hợp với khe mảng có liên quan được điền bằng các giá trị. Cho mã của bạn một ví dụ nhỏ cho thấy những gì diễn ra. Vui lòng xem nội dung bình luận. Đây là một bản phát hành.

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

Đoạn mã trên được JIT biên soạn như sau

// 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 

Vì vậy, trong ngắn hạn, mã tải các hằng số trong thanh ghi và sau đó sao chép giá trị của các thanh ghi vào bộ nhớ gắn liền với một phần liên quan của thể hiện mảng.

1

foos[1] sẽ chứa bản sao bit của new Foo(20, 10);.

+0

để biểu thức (mới Foo (20, 10)) đánh giá giá trị và sau đó nó được sao chép vào vị trí foos [1]? – CodeFusionMobile

+0

Có. Câu trả lời của Brian Rasmussen cho thấy điều gì xảy ra sâu bên trong mã. –

0

Tạo một mảng cấu trúc tạo trong mỗi vị trí giá trị mặc định. Trong C#, gọi new trên cấu trúc trong C# tạo một cá thể tạm thời mới (rất có thể trên ngăn xếp) và gán một cấu trúc cho một cấu trúc khác luôn thay đổi trường hợp sau bằng cách ghi đè tất cả các trường sau cùng với nội dung của các trường tương ứng trong trước đây. Do đó, câu lệnh foos[1] = new Foo(20,10); tạo phiên bản tạm thời mới là Foo với giá trị mặc định, chuyển trường hợp đó tới hàm tạo tham số, sao chép tất cả các trường của cá thể tạm thời đó vào cá thể được giữ trong vùng mảng 1 và sau đó loại bỏ cá thể tạm thời.

Ngẫu nhiên, hành vi của câu lệnh tương ứng trong vb.net hơi khác một chút. Nói foos(1) = New Foo(20,10) sẽ đặt lại tất cả các trường của foos(1) thành giá trị mặc định của chúng, sau đó vượt qua foos(1) đến hàm tạo tham số. Sự khác biệt này có thể đáng kể nếu bất kỳ mã nào cố truy cập foos(1) trong khi hàm tạo đang chạy.

Các vấn đề liên quan