Czy istnieje jakiś powód, aby uwidaczniać kolekcję wewnętrzną jako ReadOnlyCollection, a nie IEnumerable, jeśli kod wywołujący wykonuje iterację tylko po kolekcji?
class Bar
{
private ICollection<Foo> foos;
// Which one is to be preferred?
public IEnumerable<Foo> Foos { ... }
public ReadOnlyCollection<Foo> Foos { ... }
}
// Calling code:
foreach (var f in bar.Foos)
DoSomething(f);
Jak widzę, IEnumerable jest podzbiorem interfejsu ReadOnlyCollection i nie pozwala użytkownikowi modyfikować kolekcji. Więc jeśli interfejs IEnumberable jest wystarczający, to jest ten, którego należy użyć. Czy to właściwy sposób rozumowania, czy czegoś mi brakuje?
Dzięki / Erik
c#
.net
collections
ienumerable
readonly-collection
Erik Öjebo
źródło
źródło
ReadOnlyCollection
jeśli masz paranoję, ale teraz nie jesteś przywiązany do konkretnej implementacji.Odpowiedzi:
Bardziej nowoczesne rozwiązanie
O ile nie potrzebujesz, aby kolekcja wewnętrzna była zmienna, możesz użyć
System.Collections.Immutable
pakietu, zmienić typ pola na niezmienną kolekcję, a następnie ujawnić to bezpośrednio -Foo
oczywiście zakładając , że jest niezmienny.Zaktualizowana odpowiedź, aby bardziej bezpośrednio odpowiedzieć na pytanie
To zależy od tego, jak bardzo ufasz kodowi dzwoniącemu. Jeśli masz pełną kontrolę nad wszystkim, co kiedykolwiek zadzwoni do tego członka i gwarantujesz, że żaden kod nigdy nie użyje:
to z pewnością nic się nie stanie, jeśli po prostu zwrócisz kolekcję bezpośrednio. Jednak generalnie staram się być trochę bardziej paranoikiem.
Podobnie, jak mówisz: jeśli tylko potrzebujesz
IEnumerable<T>
, to po co przywiązywać się do czegoś mocniejszego?Oryginalna odpowiedź
Jeśli używasz .NET 3.5, możesz uniknąć tworzenia kopii i prostego rzutowania, używając prostego wywołania Skip:
(Istnieje wiele innych opcji zawijania w trywialny sposób - fajną rzeczą
Skip
w przypadku opcji Select / Where jest to, że nie ma delegata do bezcelowego wykonywania dla każdej iteracji).Jeśli nie używasz .NET 3.5, możesz napisać bardzo prosty wrapper, który zrobi to samo:
źródło
ReadOnlyCollection
do an,ICollection
a następnie działanoAdd
pomyślnie. O ile wiem, nie powinno to być możliwe.evil.Add(...)
Powinny rzucać się błąd. dotnetfiddle.net/GII2jWReadOnlyCollection
- rzucam,IEnumerable<T>
który w rzeczywistości nie jest tylko do odczytu ... zamiast ujawniaćReadOnlyCollection
ReadOnlyCollection
zamiast anIEnumerable
, aby chronić się przed rzucaniem zła.Jeśli potrzebujesz tylko iterować po kolekcji:
wtedy wystarczy zwrócić IEnumerable .
Jeśli potrzebujesz losowego dostępu do przedmiotów:
następnie zawiń go w ReadOnlyCollection .
źródło
System.Collections.Immutable
zgodnie z zaleceniami Jona Skeeta jest całkowicie uzasadnione, gdy ujawniasz coś, co nigdy się nie zmieni po uzyskaniu do niego odniesienia. Z drugiej strony, jeśli ujawniasz widok tylko do odczytu zbioru zmiennego, to ta rada jest nadal ważna, chociaż prawdopodobnie ujawniłbym ją jakoIReadOnlyCollection
(która nie istniała, kiedy to pisałem).bar.Foos[17]
zIReadOnlyCollection
. Jedyną różnicą jest dodatkowaCount
nieruchomość.public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
bar.Foos[17]
, musisz to ujawnić jakoReadOnlyCollection<Foo>
. Jeśli chcesz poznać rozmiar i iterować przez niego, wyświetl go jakoIReadOnlyCollection<Foo>
. Jeśli nie musisz znać rozmiaru, użyjIEnumerable<Foo>
. Istnieją inne rozwiązania i inne szczegóły, ale wykraczają one poza zakres dyskusji na temat tej konkretnej odpowiedzi.Jeśli to zrobisz, nic nie powstrzyma twoich rozmówców przed rzutowaniem IEnumerable z powrotem do ICollection, a następnie zmodyfikowaniem go. ReadOnlyCollection usuwa tę możliwość, chociaż nadal można uzyskać dostęp do podstawowej zapisywalnej kolekcji za pośrednictwem odbicia. Jeśli kolekcja jest niewielka, bezpiecznym i łatwym sposobem obejścia tego problemu jest zwrócenie kopii.
źródło
Unikam używania ReadOnlyCollection tak bardzo, jak to możliwe, w rzeczywistości jest to znacznie wolniejsze niż zwykłe używanie zwykłej listy. Zobacz ten przykład:
źródło
Czasami możesz chcieć użyć interfejsu, być może dlatego, że chcesz mockować kolekcję podczas testów jednostkowych. Zobacz mój wpis na blogu, aby dodać własny interfejs do ReadonlyCollection za pomocą adaptera.
źródło