W C # dobrze używam LINQ i IEnumerable. I wszystko jest w porządku (a przynajmniej w większości).
Jednak w wielu przypadkach okazuje się, że IEnumerable<X>
domyślnie potrzebuję pustego . To znaczy chciałbym
for (var x in xs) { ... }
do pracy bez konieczności sprawdzania zerowej wartości. Teraz robię to, w zależności od szerszego kontekstu:
var xs = f() ?? new X[0]; // when xs is assigned, sometimes
for (var x in xs ?? new X[0]) { ... } // inline, sometimes
Teraz, podczas gdy powyższe jest dla mnie w porządku - to znaczy, jeśli jest jakiś „dodatkowy narzut” związany z tworzeniem obiektu tablicy, po prostu nie obchodzi mnie to - zastanawiałem się:
Czy istnieje singleton „pusty niezmienny IEnumerable / IList” w C # / .NET? (A nawet jeśli nie, czy istnieje „lepszy” sposób rozwiązania opisanego powyżej przypadku?)
Java ma Collections.EMPTY_LIST
niezmienny singleton - „dobrze wpisany” via Collections.emptyList<T>()
- który służy temu celowi, chociaż nie jestem pewien, czy podobna koncepcja mogłaby działać nawet w C #, ponieważ typy generyczne są obsługiwane inaczej.
Dzięki.
public static class Array<T> { public static readonly T[] Empty = new T[0]; }
i można go nazwać jak:Array<string>.Empty
. Zapytałem o to tutaj w CodeReview .Odpowiedzi:
Szukasz
Enumerable.Empty<T>()
.W innych wiadomościach pusta lista Java jest do bani, ponieważ interfejs List udostępnia metody dodawania elementów do listy, które generują wyjątki.
źródło
List
(kolekcji, do której można uzyskać dostęp za pomocą indeksu). TychList
może być tylko do odczytu. I czasami trzeba zwrócić taki readonlyList
z zerowym elementem. Tak więcCollections.emptyList()
metoda. Nie możesz po prostu zwrócić pustej iterowalnej, ponieważ zaimplementowany interfejs określa, że metoda zwraca listę. Duży problem polega na tym, że nie ma interfejsu ImmutableList, który można zwrócić. Ten sam problem występuje w .NET: każdyIList
może być tylko do odczytu.Enumerable.Empty<T>()
jest dokładnie tym.źródło
Array.Empty<T>()
jest dokładniejsza, ponieważ jest toIList<T>
. Ma tę szczęśliwą zaletę, że jest również satysfakcjonującaIReadOnlyCollection<T>
. Oczywiście, gdyIEnumerable<T>
robi wystarczyćEnumerable.Empty<T>()
pasuje idealnie.Array.Empty<T>
udostępnieniem. :) W każdym razie, niezależnie od tytułu, pytanie wyraźnie wskazuje, że jestIEnumerable<T>
on dobry do celu.Myślę, że szukasz
Enumerable.Empty<T>()
.Pusta lista singletona nie ma większego sensu, ponieważ listy są często zmienne.
źródło
W oryginalnym przykładzie używasz pustej tablicy, aby zapewnić pustą wyliczalną. Chociaż używanie
Enumerable.Empty<T>()
jest całkowicie słuszne, mogą istnieć inne przypadki: jeśli musisz użyć tablicy (lubIList<T>
interfejsu), możesz użyć metodyco pomaga uniknąć niepotrzebnych przydziałów.
Uwagi / odniesienia:
źródło
Myślę, że dodanie metody rozszerzenia jest czystą alternatywą dzięki ich zdolności do obsługi wartości null - coś takiego:
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> list) { return list ?? Enumerable.Empty<T>(); } foreach(var x in xs.EmptyIfNull()) { ... }
źródło
Microsoft zaimplementował `` Any () '' w ten sposób ( źródło )
public static bool Any<TSource>(this IEnumerable<TSource> source) { if (source == null) throw new ArgumentNullException("source"); using (IEnumerator<TSource> e = source.GetEnumerator()) { if (e.MoveNext()) return true; } return false; }
Jeśli chcesz zapisać wywołanie na stosie wywołań, zamiast pisać wywoływaną metodę rozszerzającą
!Any()
, po prostu przepisz, wprowadź te trzy zmiany:public static bool IsEmpty<TSource>(this IEnumerable<TSource> source) //first change (name) { if (source == null) throw new ArgumentNullException("source"); using (IEnumerator<TSource> e = source.GetEnumerator()) { if (e.MoveNext()) return false; //second change } return true; //third change }
źródło
Używanie
Enumerable.Empty<T>()
z listami ma wadę. Jeśli przekażeszEnumerable.Empty<T>
konstruktor listy, zostanie przydzielona tablica o rozmiarze 4. Ale jeśli podasz pustyCollection
do konstruktora listy, nie nastąpi alokacja. Jeśli więc używasz tego rozwiązania w całym kodzie, najprawdopodobniej jeden z nichIEnumerable
zostanie użyty do skonstruowania listy, co spowoduje niepotrzebne alokacje.źródło