2015-05-16 13 views
10

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:

  1. 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.

  2. 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.

+0

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

+0

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 –

Trả lời

2

mẫu nào được coi là tốt nhất khi đi qua các bộ sưu tập không thể thay đổi?

Tôi nghĩ câu trả lời cho câu hỏi của bạn là IReadOnlyCollection<T>, sẽ được trải rộng trong .NET 4.6. Bằng cách vượt qua một bộ sưu tập chỉ đọc, bạn có thể vừa giữ được tính bất biến và vẫn làm việc với các bộ sưu tập thường xuyên triển khai giao diện đó.

+0

Cảm ơn Yuval. Điều đó có vẻ như là một gợi ý tốt đẹp mặc dù tôi tự hỏi làm thế nào tốt nó thực sự là lúc thực thi immutablity. ReadOnlyCollection rõ ràng thực hiện giao diện IReadOnlyCollection nhưng tài liệu MSDN (https://msdn.microsoft.com/en-us/library/ms132474(v=vs.110).aspx) làm cho nó rõ ràng rằng ReadOnlyCollection chỉ là một wrapper xung quanh một mutable vật. Do đó, việc sử dụng IReadOnlyCollection sẽ vẫn xuất hiện để cho phép một đối tượng có thể thay đổi được chuyển qua API. – NeilMacMullen

+0

@NeilMacMullen Tôi nghĩ rằng đi qua xung quanh là IROC là rõ ràng trong câu nói "này shoyld chỉ được sử dụng như là một bản sao đọc". Có ai thực sự cố gắng để biến đổi một giao diện như vậy? –

+1

Nhà cung cấp IROC đang tự bảo vệ mình một cách hiệu quả chống lại người tiêu dùng vô tình làm thay đổi bộ sưu tập. Tuy nhiên, người tiêu dùng không nhận được sự bảo đảm tương tự. Theo nghĩa đó, đó là hợp đồng yếu hơn một API hoàn toàn không thay đổi, cung cấp sự đảm bảo cho cả người tiêu dùng và nhà cung cấp. – NeilMacMullen

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