Gibt es in C # einen Singleton "Leere Liste"?

79

In C # verwende ich LINQ und IEnumerable ein gutes Stück. Und alles ist gut und schön (oder zumindest meistens).

In vielen Fällen stelle ich jedoch fest, dass ich IEnumerable<X>standardmäßig ein Leerzeichen benötige . Das heißt, ich würde gerne

for (var x in xs) { ... }

zu arbeiten, ohne eine Nullprüfung zu benötigen. Dies ist, was ich derzeit mache, abhängig vom größeren Kontext:

var xs = f() ?? new X[0];              // when xs is assigned, sometimes
for (var x in xs ?? new X[0]) { ... }  // inline, sometimes

Nun, obwohl das oben Genannte für mich vollkommen in Ordnung ist - das heißt, wenn es beim Erstellen des Array-Objekts einen "zusätzlichen Aufwand" gibt, ist es mir einfach egal -, habe ich mich gefragt:

Gibt es in C # /. NET den Singleton "leerer unveränderlicher IEnumerable / IList"? (Und selbst wenn nicht, gibt es eine "bessere" Möglichkeit, den oben beschriebenen Fall zu behandeln?)

Java hat einen Collections.EMPTY_LISTunveränderlichen Singleton - "gut typisiert" über Collections.emptyList<T>()-, der diesem Zweck dient, obwohl ich nicht sicher bin, ob ein ähnliches Konzept überhaupt in C # funktionieren könnte, da Generika anders behandelt werden.

Vielen Dank.


quelle
Nun, verdammt :) Das ist es, was ich bekomme, wenn ich mich auf List / IList und nicht Enumerable / IEnumerable konzentriere, danke an alle - Stimmen rundum.
2
public static class Array<T> { public static readonly T[] Empty = new T[0]; }und es kann wie folgt aufgerufen werden : Array<string>.Empty. Ich habe hier in CodeReview danach gefragt .
Şafak Gür

Antworten:

97

Sie suchen Enumerable.Empty<T>().

In anderen Nachrichten ist die leere Java-Liste zum Kotzen, weil die List-Schnittstelle Methoden zum Hinzufügen von Elementen zur Liste bereitstellt, die Ausnahmen auslösen.

Stilgar
quelle
Das ist ein sehr gültiger Punkt! Vielleicht rechtfertigt es eine neue SO-Frage?
1
Du warst ein paar Sekunden langsam und kein Dokumentationslink * Finger Shake * ;-) Aber die Akzeptanz für die Erweiterung auf svicks Kommentar.
Auch die anderen Jungs haben mehr positive Stimmen bekommen, also sind wir sogar :) Ich würde eine Frage posten, aber ich gehe jetzt ins Bett und werde der Frage nicht folgen können. Warum nicht selbst posten?
Stilgar
32
Weil eine Klassenmethode möglicherweise a zurückgeben muss List(eine Sammlung, auf die über den Index zugegriffen werden kann). Diese Listkönnen nur gelesen werden. Und manchmal müssen Sie ein solches schreibgeschütztes ListElement mit dem Element Null zurückgeben. Also die Collections.emptyList()Methode. Sie können nicht einfach eine leere Iterable zurückgeben, da eine implementierte Schnittstelle angibt, dass die Methode eine Liste zurückgibt. Das große Problem ist, dass es keine ImmutableList-Schnittstelle gibt, die Sie zurückgeben können. Das gleiche Problem besteht in .NET: IListJedes kann schreibgeschützt sein.
Laurent Bourgault-Roy
6
Nebenbei bemerkt, jedes Array ist ein IList <correspondingType> in C #, und dies wird ausgelöst, wenn Sie versuchen, auch neue Elemente hinzuzufügen.
Grzenio
52

Enumerable.Empty<T>() ist genau das.

Jon
quelle
3
Nun, nicht genau . :) Da nach einer "Liste" gefragt wurde, Array.Empty<T>()ist diese genauer, da es sich um eine handelt IList<T>. Es hat den glücklichen Vorteil, auch befriedigend zu sein IReadOnlyCollection<T>. Natürlich, wo ein IEnumerable<T> tut genügen, Enumerable.Empty<T>()passt perfekt.
Timo
2
@ Timo diese Frage und viele der Antworten, einschließlich dieser, wurden ungefähr 3 Jahre zuvor geschrieben Array.Empty<T>und zur Verfügung gestellt. :) Ungeachtet des Titels macht die Frage auf jeden Fall ausdrücklich klar, dass ein IEnumerable<T>für den Zweck gut ist.
Jon
19

Ich denke du suchst Enumerable.Empty<T>().

Ein Singleton mit leeren Listen macht nicht so viel Sinn, da Listen oft veränderlich sind.

svick
quelle
12

In Ihrem ursprünglichen Beispiel verwenden Sie ein leeres Array, um eine leere Aufzählung bereitzustellen. Bei der Verwendung von Enumerable.Empty<T>()rechts ist perfekt, es könnten auch andere Fälle: Wenn Sie verwenden müssen ein Array (oder die IList<T>Schnittstelle), können Sie die Methode verwenden ,

System.Array.Empty<T>()

Dies hilft Ihnen, unnötige Zuordnungen zu vermeiden.

Anmerkungen / Referenzen:

ventiseis
quelle
10

Ich denke, das Hinzufügen einer Erweiterungsmethode ist eine saubere Alternative, da sie mit Nullen umgehen können - so etwas wie:

  public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> list)
  {
    return list ?? Enumerable.Empty<T>();
  }

  foreach(var x in xs.EmptyIfNull())
  {
    ...
  }
Andrew Hanlon
quelle
1

Microsoft hat "Any ()" wie folgt implementiert ( Quelle )

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;
}

Wenn Sie einen Aufruf auf dem Aufrufstapel speichern möchten, anstatt eine aufrufende Erweiterungsmethode zu !Any()schreiben, schreiben Sie diese drei Änderungen einfach neu:

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
}
user2023861
quelle
1

Die Verwendung Enumerable.Empty<T>()mit Listen hat einen Nachteil. Wenn Sie Enumerable.Empty<T>den Listenkonstruktor übergeben, wird ein Array der Größe 4 zugewiesen. Wenn Sie dem Listenkonstruktor jedoch ein CollectionLeerzeichen übergeben, erfolgt keine Zuordnung. Wenn Sie diese Lösung im gesamten Code verwenden, wird höchstwahrscheinlich eines der IEnumerables zum Erstellen einer Liste verwendet, was zu unnötigen Zuordnungen führt.

sjb-sjb
quelle