Trước bất biến, IEnumerable là giao diện truy cập trong nhiều API vì điều này có lợi thế là API không nhạy cảm với loại thực tế của đối tượng được truyền.Mô hình tốt nhất để truyền các Bộ sưu tập bất biến qua API
public void DoSomeEnumerationsWithACollection(IEnumerable<Thing> collectionOfThings)
{
foreach (var thing in collectionOfThings) doSomethingWith(thing);
foreach (var thing in collectionOfThings) doSomethingElseWith(thing);
}
Tất nhiên có ít nhất hai nhược điểm này:
Mã đằng sau API không thể dựa vào sự bất biến của collectionOfThings và có thể gặp phải một "bộ sưu tập sửa đổi" ngoại lệ hoặc nhấn các vấn đề tinh tế khác.
Chúng tôi không biết liệu collectionOfThings là bộ sưu tập thực hay chỉ đơn giản là truy vấn bị trì hoãn. Nếu chúng ta cho rằng đó là một bộ sưu tập thực sự và nó không phải là chúng ta chạy nguy cơ làm giảm hiệu suất bằng cách chạy nhiều liệt kê. Nếu chúng ta cho rằng đó là một truy vấn bị trì hoãn và nó thực sự là một bộ sưu tập thực sự thì biến nó thành một danh sách cục bộ hoặc bộ sưu tập đông lạnh khác phát sinh chi phí không cần thiết mặc dù nó giúp bảo vệ chúng ta trước vấn đề đầu tiên (vẫn còn một điều kiện chủng tộc trong khi thực hiện hoạt động "ToList"). Rõ ràng chúng ta có thể viết một số lượng nhỏ mã để kiểm tra điều này và cố gắng làm "điều đúng" nhưng điều này gây phiền phức thêm lộn xộn.
Tôi phải thừa nhận tôi chưa bao giờ tìm thấy mẫu thỏa đáng để giải quyết vấn đề này ngoài việc sử dụng quy ước đặt tên. Cách tiếp cận thực dụng dường như là IEnumerable là cách tiếp cận ma sát thấp nhất để truyền xung quanh các bộ sưu tập, bất chấp các nhược điểm.
Bây giờ, với bộ sưu tập bất biến, tình hình đang được cải thiện nhiều ...
public void DoSomeEnumerationsWithACollection(ImmutableList<Thing> collectionOfThings)
{
Không còn là một nguy cơ điều chỉnh việc thu thập và không có sự mơ hồ về tác động hiệu suất của nhiều kiểu liệt kê.
Tuy nhiên, chúng tôi dường như đã mất tính linh hoạt trên API vì bây giờ chúng tôi phải chuyển vào một số ImmutableList. Nếu khách hàng của chúng tôi có một số loại bộ sưu tập bất biến có thể đếm được, nó sẽ phải được sao chép vào ImmutableList để được tiêu thụ mặc dù tất cả những gì chúng tôi muốn làm là liệt kê nó.
Lý tưởng nhất là chúng tôi có thể sử dụng một giao diện giống như
public void DoSomeEnumerationsWithACollection(IImmutableEnumerable<Thing> collectionOfThings)
nhưng tất nhiên, một giao diện không thể thực thi ngữ nghĩa như không thay đổi, ngoại trừ theo quy ước.
Sử dụng một lớp cơ sở có thể làm việc
public void DoSomeEnumerationsWithACollection(ImmutableEnumerableBase<Thing> collectionOfThings)
ngoại trừ việc nó được coi là hình thức xấu để tạo ra các lớp học bất biến unsealed kẻo một lớp con giới thiệu mutability. Và trong mọi trường hợp, điều này đã không được thực hiện trong BCL.
Hoặc chúng ta chỉ có thể tiếp tục sử dụng IEnumerable trong API và sử dụng một quy ước đặt tên để làm cho nó rõ ràng mã của chúng tôi dựa vào một bộ sưu tập bất biến để được thông qua trong.
... Vì vậy,câu hỏi của tôi là những gì mô hình được coi là tốt nhất khi đi qua các bộ sưu tập bất biến? Có phải ImmutableList "mới IEnumerable" khi chúng tôi bắt đầu sử dụng tính không thay đổi hoặc có cách nào tốt hơn không?
Cập nhật
IReadOnlyCollection (được đề xuất bởi Yuval Itzchakov dưới đây) là một cải tiến rõ rệt so với IEnumerable nhưng vẫn không bảo vệ đầy đủ cho người tiêu dùng chống lại những thay đổi không kiểm soát được trong bộ sưu tập. Điều đáng chú ý là codebase Roslyn sử dụng nhiều bất biến (chủ yếu là thông qua ImmutableArray) và xuất hiện để sử dụng cách đánh máy rõ ràng khi chuyển chúng vào các phương thức khác mặc dù có một vài địa điểm nơi ImmutableList s được chuyển vào các phương thức chấp nhận IEnumerable.
Mặc dù hóa ra có các giao diện bất biến trong BCL (IImmutableList, IImmutableSet) dường như không có giao diện chung cho các lớp không thể đếm được liệt kê. – NeilMacMullen
Bạn thường có thể kiểm tra xem một bộ sưu tập có thực sự là gì không, nếu nó thực hiện ICollection –