2015-03-26 13 views
5

Các lựa chọn thay thế bất biến cho một mảng mã sản xuất trong C# (ít nhất là trong khi ImmutableArray là bản beta)?Lựa chọn thay thế bất biến cho một mảng trong C# là gì?

+2

Mục tiêu bạn đang cố đạt được là gì? Có lẽ đi qua một 'IEnumerable' thay vì một mảng có thể giải quyết vấn đề của bạn? –

+0

Tôi đoán mảng nhanh chóng nhận được số lượng và đọc tất cả nội dung trong một lần (trong trường hợp cụ thể của chúng, chúng sẽ nằm trong bộ nhớ và được truy cập bởi một số chủ đề, thường chứa 10-1000 mục) – karl25233

Trả lời

7

Bạn nên cung cấp thêm chi tiết, nhưng một tùy chọn là System.Collections.ObjectModel.ReadOnlyCollection<> hoạt động như một danh sách/mảng. Nó có thể được tạo ra như thế này:

var immutable1 = Array.AsReadOnly(new[] { 7, 9, 13, }); 
    var immutable2 = (new List<int> { 7, 9, 13, }).AsReadOnly(); 

Nó là một gói bao quanh một IList<>. Nó không thể được truyền tới bất cứ thứ gì cho phép thay đổi danh sách/mảng bên dưới. Nhưng đối tượng cơ bản có thể đạt được với sự phản chiếu, tất nhiên.

Nếu ai đó ở nơi khác thay đổi danh sách/mảng "bên trong", sẽ được tinh chỉnh trong trình bao bọc ReadOnlyCollection<>. Không có bản sao nào được thực hiện.

Đây là hình thức bất biến "nông". Nếu các mục riêng lẻ có thể thay đổi (không thể int), trình bao bọc sẽ không cấm điều đó.


Một điều rẻ hơn để làm là chỉ cần sử dụng giao diện IReadOnlyList<out T> đó là hiệp biến trong T (thêm một lợi thế). Cả hai List<T>T[] triển khai giao diện này. Vì vậy:

IReadOnlyList<int> immutable3 = new[] { 7, 9, 13, }; 
    IReadOnlyList<int> immutable4 = new List<int> { 7, 9, 13, }; 

Trong trường hợp đó, tuy nhiên, người tiêu dùng có thể kiểm tra immutable3 as IList<int>, ví dụ, hoặc immutable3 as int[], và có được một "có thể thay đổi kiểu tài liệu tham khảo" cho các đối tượng có thể thay đổi (không phản ánh).


Tuy nhiên, có loại không thay đổi (ví dụ như ImmutableList<>) mà không phải là "trong phiên bản beta" trong Microsoft.Immutable.Collections. Vì vậy, bạn có thể xem xét cài đặt đó nếu bạn thực sự cần nó. Nó không phải là một phần của thư viện .NET chuẩn, vì vậy bạn sẽ phải cài đặt nó một cách riêng biệt.


Để giải thích ý tôi bằng cách "cạn", giả sử bạn có một mảng có loại phần tử trong chính nó có thể thay đổi. Sau đó, không có vấn đề mà các đề xuất ở trên bạn sử dụng, mọi người vẫn có thể modyfy các mục. Ví dụ:

StringBuilder[] yourArray = { 
    new StringBuilder("All"), 
    new StringBuilder("is"), 
    new StringBuilder("good"), 
    }; 

var immutable = ImmutableList<StringBuilder>.Empty.AddRange(yourArray); 

immutable[0].Insert(0, "NOT "); 
immutable[1].Clear(); 
immutable[2].Append(new string('!', 10000)); 
+1

Ngoài ra, lưu ý rằng bộ sưu tập chỉ đọc không phải là bản sao. Vì vậy, bạn phải chắc chắn rằng danh sách/mảng cơ bản không bị rò rỉ, bởi vì điều đó sẽ làm thay đổi trình bao bọc chỉ đọc. – Luaan

+0

@Luaan Điểm tốt. Tôi đã đề cập đến nó trong câu trả lời của tôi ngay bây giờ. –

+0

Bộ sưu tập không thể thay đổi Gói NuGet * vẫn * beta (hoặc ít nhất là trạng thái RC): "System.Collections.Immutable 1.1.34 ** - rc **" – Brandon

1

Những bất lợi đến chỉ sử dụng ReadOnlyCollection hoặc IReadOnlyList là những cung cấp đảm bảo yếu hơn nhiều so với một mảng bất biến. Các đối tượng này chỉ đảm bảo rằng người giữ bộ sưu tập chỉ đọc sẽ không sửa đổi nó. Họ không đảm bảo rằng bộ sưu tập sẽ không được sửa đổi bởi mã giữ một tham chiếu đến bộ sưu tập cơ bản.

Cụ thể, người tiêu dùng đối tượng như vậy vẫn phải sử dụng đồng bộ hóa trong khi đọc từ bộ sưu tập, vì không đảm bảo rằng chủ sở hữu của bộ sưu tập cơ bản không bận thêm/xóa mục khỏi bộ sưu tập khi bạn đọc nó qua lense chỉ đọc của bạn. Hoặc bạn phải có một thỏa thuận được tài liệu giữa người tiêu dùng và nhà sản xuất rằng bộ sưu tập sẽ không bị sửa đổi.

Nếu bạn thực sự muốn một mảng bất biến và bạn không thể sử dụng gói Bộ sưu tập không thể thay đổi vì lý do nào đó, thì hãy đảm bảo nguồn không bao giờ sửa đổi mảng, mà thay vào đó nhân bản nó bất cứ khi nào muốn thay đổi. Kết hợp điều này với việc đưa ra các xử lý "chỉ đọc" để thực hiện các mảng không thể thay đổi.

LINQ làm cho nó khá dễ dàng để thực hiện của riêng bạn, mặc dù nó không phải là terribly thông minh. Ví dụ về việc xóa một mục trong một mảng với một mục khác theo cách không thay đổi:

private int[] _array; 

public void RemoveItem(int item) 
{ 
    _array = _array.Where(x => x != item).ToArray(); 
} 

/// <summary>Gets immutable array</summary> 
public IReadOnlyList<int> Data { get { return _array; } } 
+0

Ok, cảm ơn thông tin :) Tôi đoán ImmutableList sẽ là lựa chọn tốt nhất hiện nay. – karl25233

+0

Một cách để đối phó với mối quan tâm đó là sao chép toàn bộ danh sách/mảng và sau đó bọc _copy_ trong một 'ReadOnlyCollection <>' (hoặc upcast bản sao vào 'IReadOnlyList <> '). Nếu bạn viết nó như 'var safe = originalArray.ToList(). AsReadOnly();' bạn có thể chắc chắn rằng không ai có một tham chiếu đến trung gian 'List <>' được tạo ra (vì bạn không giữ tham chiếu đó). –

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