2015-08-03 13 views
7

xem xét sau hai kiểu dữ liệu:gì là sự khác biệt giữa indexer mảng và bất kỳ indexer đối tượng khác

class C 
{ 
    public int I { get; set; } 
} 

struct S 
{ 
    public int I { get; set; } 
} 

Hãy cố gắng sử dụng chúng trong danh sách, ví dụ:

var c_list = new List<C> { new C { I = 1 } }; 
c_list[0].I++; 

var s_list = new List<S> { new S { I = 1 } }; 
s_list[0].I++; // (a) CS1612 compilation error 

Đúng như dự đoán, có là lỗi biên dịch trên dòng (a): CS1612 Cannot modify the return value of 'List<UserQuery.S>.this[int]' because it is not a variable. Điều này là tốt, bởi vì thực sự chúng tôi đang cố gắng sửa đổi bản sao tạm thời của S, đó là giá trị r trong việc đưa ra ngữ cảnh.

Nhưng chúng ta hãy cố gắng làm điều tương tự cho một mảng:

var c_arr = new[] { new C { I = 1 } }; 
c_arr[0].I++; 

var s_arr = new[] { new S { I = 1 } }; 
s_arr[0].I++; // (b) 

Và .. làm việc này.

Nhưng

var s_arr_list = (IList<S>) s_arr; 
s_arr_list[0].I++; 

sẽ không biên dịch, như mong đợi.

Nếu chúng ta nhìn vào IL sản xuất, chúng ta sẽ thấy sau đây:

IL_0057: ldloc.1  // s_arr 
IL_0058: ldc.i4.0 // index 
IL_0059: ldelema  UserQuery.S // manager pointer of element 

ldelema tải địa chỉ của phần tử mảng để phía trên cùng của ngăn xếp đánh giá. Hành vi như vậy được mong đợi với các mảng fixed và các con trỏ không an toàn. Nhưng đối với bối cảnh an toàn thì đây là một chút bất ngờ. Tại sao có một trường hợp không rõ ràng đặc biệt cho mảng? Bất kỳ lý do tại sao không có tùy chọn để đạt được hành vi tương tự cho các thành viên của các loại khác?

+0

Tại sao bạn thấy 'ldelema' bất ngờ cho ngữ cảnh an toàn? Nó tải một tham chiếu được quản lý (do đó được kiểm soát bởi GC), như 'ldloca' hoặc' ldflda'. Khi bạn sử dụng 'ref' trong C#, đây chính xác là nó. – IllidanS4

Trả lời

8

Biểu thức truy cập mảng được phân loại là một biến. Bạn có thể gán cho nó, vượt qua nó bằng cách tham khảo vv Một truy cập indexer được phân loại riêng biệt ... trong danh sách phân loại (C# 5 đặc tả phần 7.1.)

  • Một truy cập indexer. Mỗi truy cập chỉ mục có một kiểu liên kết, cụ thể là kiểu phần tử của trình chỉ mục. Hơn nữa, một truy cập chỉ mục có một biểu thức thể hiện được liên kết và một danh sách đối số liên kết. Khi một accessor (khối get hoặc set) của một truy cập indexer được gọi, kết quả của việc đánh giá biểu thức thể hiện trở thành cá thể được biểu diễn bởi điều này (§7.6.7), và kết quả đánh giá danh sách đối số sẽ trở thành danh sách tham số sự kêu gọi.

Hãy suy nghĩ về điều này như tương tự như sự khác biệt giữa một cánh đồng và một tài sản:

public class Test 
{ 
    public int PublicField; 
    public int PublicProperty { get; set; } 
} 

... 

public void MethodCall(ref int x) { ... } 

... 

Test test = new Test(); 
MethodCall(ref test.PublicField); // Fine 
MethodCall(ref test.PublicProperty); // Not fine 

Về cơ bản, một indexer là một cặp phương pháp (hoặc một duy nhất) trong khi một truy cập mảng cung cấp cho bạn một vị trí lưu trữ.

Lưu ý rằng nếu bạn không sử dụng cấu trúc có thể thay đổi để bắt đầu, bạn sẽ không thấy sự khác biệt theo cách này - tôi khuyên bạn không nên sử dụng cấu trúc có thể thay đổi được.

1

Trình chỉ mục lớp giống như trong List<T> thực sự là một cách thuận tiện về cú pháp gọi phương thức.

Tuy nhiên, với mảng, bạn thực sự truy cập vào cấu trúc trong bộ nhớ. Không có cuộc gọi phương thức nào trong trường hợp đó.

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