.net - IEnumerable ist leer?



linq (6)

Ich weiß, dass es für die meiste Zeit wahrscheinlich keine Rolle spielt / beeinflusst, aber ich hasse die Idee, eine IEnumerable und .Count() tun. Gibt es eine IsEmpty oder NotEmpty oder eine Funktion? (ähnlich wie stl empty ())


Bei IEnumerable oder IEnumerable<T> , nein.

Aber es macht wirklich keinen Sinn. Wenn eine Auflistung leer ist und Sie versuchen, mithilfe von IEnumerable zu iterieren, gibt der Aufruf von IEnumerator.MoveNext() einfach false ohne Leistungskosten zurück.


Denken Sie daran, dass IEnumerable nur eine Schnittstelle ist. Die Implementierung dahinter kann von Klasse zu Klasse sehr unterschiedlich sein (siehe Joes Beispiel). Die Erweiterungsmethode IEnumerable.Any () muss ein generischer Ansatz sein und ist möglicherweise nicht das, was Sie möchten (leistungsmäßig). Yossarian schlägt ein Mittel vor, das für viele Klassen geeignet sein sollte. Wenn die zugrunde liegende Implementierung jedoch nicht "Rendite" verwendet, können Sie trotzdem einen Preis zahlen.

Wenn Sie sich an Sammlungen oder Arrays halten, die in einer IEnumerable-Schnittstelle eingeschlossen sind, haben Cristobalito und Yossarian im Allgemeinen die besten Antworten. Meine Vermutung ist die eingebaute .Any () ext-Methode, die von Yossarian empfohlen wird.


Ohne LINQ können Sie Folgendes tun:

bool IsEmpty(IEnumerable en)
{
    foreach(var c in en) { return false; }
    return true;
}

Sie können Erweiterungsmethoden wie Any () oder Count () verwenden. Count () ist teurer als Any (), da es die gesamte Aufzählung ausführen muss, wie andere darauf hingewiesen haben.

Bei einer faulen Bewertung (z. B. bei einer Methode, die den Ertrag verwendet) kann dies jedoch kostspielig sein. Bei der folgenden IEnumerable Implementierung entstehen beispielsweise bei jedem Aufruf von Any oder Count die Kosten für einen neuen Roundtrip zur Datenbank:

IEnumerable<MyObject> GetMyObjects(...)
{
    using(IDbConnection connection = ...)
    {
         using(IDataReader reader = ...)
         {
             while(reader.Read())
             {
                 yield return GetMyObjectFromReader(reader);
             }
         }
    }
}

Ich denke, die Moral ist:

  • Wenn Sie nur über ein IEnumerable<T> verfügen und mehr als nur aufzählen möchten (z. B. Count oder Any verwenden), sollten Sie es zuerst in eine Liste konvertieren (Erweiterungsmethode ToList). Auf diese Weise garantieren Sie, dass Sie nur einmal aufzählen.

  • Wenn Sie eine API entwerfen, die eine Sammlung zurückgibt, sollten Sie ICollection<T> (oder sogar IList<T> ) anstelle von IEnumerable<T> da viele Leute dies zu empfehlen scheinen. Auf diese Weise verstärken Sie Ihren Vertrag, um keine faulen Bewertungen (und somit keine Mehrfachbewertung) zu gewährleisten.

Bitte beachten Sie, ich sage, Sie sollten die Rückgabe einer Sammlung in Betracht ziehen , nicht immer eine Sammlung. Wie immer gibt es Kompromisse, wie aus den folgenden Kommentaren ersichtlich.

  • @KeithS meint, Sie sollten niemals mit einem DataReader nachgeben, und obwohl ich nie sage, niemals würde ich sagen, dass es generell ein ICollection<T> Rat ist, dass eine Datenzugriffsebene eine ICollection<T> anstatt eines von Lazy bewerteten IEnumerable<T> die Gründe, die KeithS in seinem Kommentar angibt.

  • @Bear Monkey weist darauf hin, dass das Instanziieren einer Liste im obigen Beispiel teuer sein kann, wenn die Datenbank eine große Anzahl von Datensätzen zurückgibt. Das trifft auch zu, und in einigen (wahrscheinlich seltenen) Fällen kann es angebracht sein, den Hinweis von @ KeithS zu ignorieren und eine von Lazy bewertete Enumeration zurückzugeben, vorausgesetzt, der Verbraucher unternimmt etwas, das nicht zu zeitaufwändig ist (z. B. das Generieren von Gesamtwerten).


Sie möchten die Erweiterungsmethode IEnumerable.Any() (.Net Framework 3.5 und höher). Es vermeidet, über die Elemente zu zählen.


Verwenden Sie Enumerable.Empty (), das anstelle von IEnumerable.Any () verhindert, dass die Liste mit Nullen endet.





ienumerable