Wie überprüfe ich, ob IEnumerable null oder leer ist?

154

Ich liebe string.IsNullOrEmptyMethode. Ich hätte gerne etwas, das die gleiche Funktionalität für IEnumerable ermöglicht. Gibt es solche? Vielleicht eine Sammelhelferklasse? Der Grund, den ich frage, ist, dass in ifAnweisungen der Code unübersichtlich aussieht, wenn das Muster ist (mylist != null && mylist.Any()). Es wäre viel sauberer zu haben Foo.IsAny(myList).

Dieser Beitrag gibt diese Antwort nicht: IEnumerable ist leer? .

Schultz9999
quelle
1
@msarchet: Ich würde Ihnen wahrscheinlich die Antwort geben, wenn dies nicht der Kommentar wäre :)
Schultz9999
Für mich scheint dies ein XY-Problem zu sein. Anstatt zu fragen, wie ich genau überall nach Null suchen kann, ohne dass dies so störend ist, sollten Sie fragen, wie ich mein Design verbessern kann, damit ich nicht überall nach Null suchen muss.
Sara
@nawfal, die Frage, mit der Sie verlinkt haben, enthält keine speziellen Nullprüfungen, daher würde ich sie nicht als Duplikat betrachten
Mygeen

Antworten:

188

Sicher könnten Sie das schreiben:

public static class Utils {
    public static bool IsAny<T>(this IEnumerable<T> data) {
        return data != null && data.Any();
    }
}

Seien Sie jedoch vorsichtig, dass nicht alle Sequenzen wiederholbar sind. Im Allgemeinen gehe ich lieber nur einmal spazieren, nur für den Fall.

Marc Gravell
quelle
12
Ist das ein gutes Muster? Ich würde das thisdort fallen lassen - ich betrachte Erweiterungsmethoden, von denen angenommen wird, dass sie nullals Zeichen für hässliches Design aufgerufen werden .
Mormegil
28
@ Mormegil Warum? Erweiterungsmethoden geben C # schließlich die Möglichkeit, mit Nullen zu arbeiten, was andere Sprachen (wie Ruby) für selbstverständlich halten.
Matt Greer
5
Warum ist das unbedingt schlecht? Wie in diesem Fall ist es manchmal sehr praktisch, da Sie damit die Dinge homogener und mit weniger Sonderfällen behandeln können.
Mr. Putty
5
@ Mormegil meh - darüber kann ich mich nicht aufregen. Solange die Absicht klar ist usw.
Marc Gravell
6
@Miryafa .Any()ist eine Erweiterungsmethode, die ausgeführt wird IEnumerable<T>(oder IQueryable<T>, obwohl dies ein anderes Szenario ist). Dadurch wird die Sequenz zumindest teilweise verbraucht (obwohl dies immer noch bedeutet, dass sie verbraucht ist) - möglicherweise muss nur ein Element gelesen werden (insbesondere, wenn kein Prädikat vorhanden ist). Da Sequenzen ( IEnumerable<T>) nicht wiederholbar sein müssen , könnte dies der Fall sein . Any()ohne Prädikat ist im Wesentlichen gleichbedeutend mit foreach(var x in sequence) { return true; } return false;- obwohl es GetEnumerator()etc anstelle der Compilersyntax verwendet
Marc Gravell
119
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) {
    return enumerable == null || !enumerable.Any();
}
Matt Greer
quelle
8
Nun, nicht ganz, das OP fragte nach IEnumerable, nicht IEnumerable <T> ;-)
yoyo
8
Ja, IEnumerablehat keine Any()Erweiterung.
Blaise
23

Hier ist eine modifizierte Version der nützlichen Antwort von @Matt Greer, die eine statische Wrapper-Klasse enthält, sodass Sie diese einfach kopieren und in eine neue Quelldatei einfügen können, nicht von Linq abhängig sind und eine generische IEnumerable<T>Überladung hinzufügen , um das Boxen von Werttypen zu vermeiden das würde mit der nicht generischen Version auftreten. [BEARBEITEN: Beachten Sie, dass die Verwendung von IEnumerable<T>das Boxen des Enumerators nicht verhindert. Die Ententypisierung kann dies nicht verhindern, aber zumindest die Elemente in einer werttypisierten Sammlung werden nicht jeweils boxen.]

using System.Collections;
using System.Collections.Generic;

public static class IsNullOrEmptyExtension
{
    public static bool IsNullOrEmpty(this IEnumerable source)
    {
        if (source != null)
        {
            foreach (object obj in source)
            {
                return false;
            }
        }
        return true;
    }

    public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
    {
        if (source != null)
        {
            foreach (T obj in source)
            {
                return false;
            }
        }
        return true;
    }
}
yoyo
quelle
15

Eine andere Möglichkeit wäre, den Enumerator abzurufen und die MoveNext () -Methode aufzurufen, um festzustellen, ob Elemente vorhanden sind:

if (mylist != null && mylist.GetEnumerator().MoveNext())
{
    // The list is not null or empty
}

Dies funktioniert sowohl für IEnumerable als auch für IEnumerable <T>.

Darren
quelle
4
Sollten Sie dispose für diesen Enumerator aufrufen? Ist die Sammlung Multithreading-fähig? Ja. stackoverflow.com/questions/13459447/…
TamusJRoyce
2
@TamusJRoyce Beachten Sie, dass Ihre Aussage nur für gilt IEnumerable<T>, da nicht generische IEnumerablenicht implementiert werden IDisposable.
Ian Kemp
9

So mache ich es und nutze einige moderne C # -Funktionen:

Option 1)

public static class Utils {
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> list) {
        return !(list?.Any() ?? false);
    }
}

Option 2)

public static class Utils {
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> list) {
        return !(list?.Any()).GetValueOrDefault();
    }
}

Übrigens, verwenden Sie niemals Count == 0oder Count() == 0nur, um zu überprüfen, ob eine Sammlung leer ist. Verwenden Sie immer Linqs.Any()

Ronald Rey
quelle
2
Count == 0 ist in Ordnung .... Vielleicht schneller als Any ()? Sie haben jedoch Recht, dass Count () == 0 schlecht ist. Für diejenigen, die sich fragen, durchläuft Count () Ihre gesamte Sammlung. Wenn es also riesig ist, kann dies eine Menge Overhead bedeuten!
Anthony Nichols
Count () iteriert die Aufzählung nur, wenn sie nicht in eine ICollection umgewandelt werden kann. Mit anderen Worten, wenn Sie diese Methode aufrufen und das Objekt bereits eine Count-Eigenschaft enthält, wird diese nur zurückgegeben, und die Leistung sollte identisch sein. Überprüfen Sie die Implementierung hier: referencesource.microsoft.com/#System.Core/System/Linq/…
Ronald Rey
Wenn Sie mit einem IEnumerable arbeiten, ist die Verwendung von Count () zum Testen der Leere definitiv eine schlechte Idee, da die Linq-Implementierung die gesamte Sammlung durchlaufen wird, während Any den Iterator nur einmal verschiebt. Beachten Sie, dass Sie die Count-Eigenschaft in diesem Fall nicht verwenden können, da sie nicht Teil der IEnumerable-Schnittstelle ist. Aus diesem Grund ist es meiner Meinung nach immer besser, Any () zu verwenden, um die Leere in allen Szenarien zu testen.
Ronald Rey
Schönes Beispiel dafür, wie ein nicht lesbarer Negationsoperator sein !kann, insbesondere in der zweiten Option;)
Fabio
6

Dies kann helfen

public static bool IsAny<T>(this IEnumerable<T> enumerable)
{
    return enumerable?.Any() == true;
}

public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
    return enumerable?.Any() != true;
}
Hossein Narimani Rad
quelle
5

Ab C # 6 können Sie die Nullweitergabe verwenden :myList?.Any() == true

Wenn Sie dies immer noch zu verstopft finden oder eine gute alte Erweiterungsmethode bevorzugen, würde ich die Antworten von Matt Greer und Marc Gravell empfehlen, der Vollständigkeit halber jedoch mit etwas erweiterter Funktionalität.

Ihre Antworten bieten die gleiche Grundfunktionalität, jedoch jeweils aus einer anderen Perspektive. Matts Antwort verwendet die string.IsNullOrEmpty-mentalität, während Marc's Antwort Linqs verwendet.Any() Weg nimmt, um die Arbeit zu erledigen.

Ich persönlich bin geneigt, die .Any()Straße zu benutzen , möchte aber die Zustandsüberprüfungsfunktionalität aus der anderen Überlastung der Methode hinzufügen :

    public static bool AnyNotNull<T>(this IEnumerable<T> source, Func<T, bool> predicate = null)
    {
        if (source == null) return false;
        return predicate == null
            ? source.Any()
            : source.Any(predicate);
    }

Sie können also immer noch Dinge tun wie: myList.AnyNotNull(item=>item.AnswerToLife == 42);wie beim normalen.Any() aber mit dem hinzugefügten Null-Check

Beachten Sie, dass mit der C # 6-Methode: eher a als a myList?.Any()zurückgibt , was der tatsächliche Effekt der Weitergabe von null istbool?bool

Thomas Mulder
quelle
1
Das Problem mit der Sammlung? .Any () ist, dass das nicht transitiv ist. Wenn null, ist die Sammlung? .Any () == true falsch, aber die Sammlung? .Any () == false ist ebenfalls falsch. Außerdem! Collection? .Any () == false ist auch false ...
Jakub Szułakiewicz
4
if (collection?.Any() == true){
    // if collection contains more than one item
}
if (collection?.Any() != true){
    // if collection is null
    // if collection does not contain any item
}
Scholtz
quelle
2

Hier ist der Code aus Marc Gravells Antwort sowie ein Beispiel für die Verwendung.

using System;
using System.Collections.Generic;
using System.Linq;

public static class Utils
{
    public static bool IsAny<T>(this IEnumerable<T> data)
    {
        return data != null && data.Any();
    }
}

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<string> items;
        //items = null;
        //items = new String[0];
        items = new String[] { "foo", "bar", "baz" };

        /*** Example Starts Here ***/
        if (items.IsAny())
        {
            foreach (var item in items)
            {
                Console.WriteLine(item);
            }
        }
        else
        {
            Console.WriteLine("No items.");
        }
    }
}

Wie er sagt, sind nicht alle Sequenzen wiederholbar, so dass Code manchmal Probleme verursachen kann, da er IsAny()anfängt, die Sequenz zu durchlaufen. Ich vermute, was Robert Harveys Antwort bedeutete, war, dass Sie oft nicht nach etwas suchen null und es leeren müssen. Oft kann man einfach nach null suchen und dann verwenden foreach.

Um zu vermeiden, dass die Sequenz zweimal gestartet und ausgenutzt wird foreach, habe ich einfach folgenden Code geschrieben:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<string> items;
        //items = null;
        //items = new String[0];
        items = new String[] { "foo", "bar", "baz" };

        /*** Example Starts Here ***/
        bool isEmpty = true;
        if (items != null)
        {
            foreach (var item in items)
            {
                isEmpty = false;
                Console.WriteLine(item);
            }
        }
        if (isEmpty)
        {
            Console.WriteLine("No items.");
        }
    }
}

Ich denke, die Erweiterungsmethode erspart Ihnen ein paar Zeilen beim Schreiben, aber dieser Code scheint mir klarer zu sein. Ich vermute, dass einige Entwickler nicht sofort erkennen würden, dass dies IsAny(items)tatsächlich die Sequenz durchläuft. (Wenn Sie viele Sequenzen verwenden, lernen Sie natürlich schnell, darüber nachzudenken, welche Schritte sie durchlaufen.)

Don Kirkby
quelle
Wenn Sie IsAny mit einer Null aufrufen, wird eine Ausnahme ausgelöst
Ace Trajkov,
3
Hast du es versucht, @Ace? Es sieht so aus, als würde es eine Ausnahme auslösen, aber Erweiterungsmethoden können für Nullinstanzen aufgerufen werden .
Don Kirkby
2

Ich benutze Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false);. Hoffe das hilft.

Nervenzusammenbruch:

Collection?.Any()wird zurückgegeben, nullwenn Collection null ist und falsewenn Collection leer ist.

Collection?.Any()??falsewird uns geben, falsewenn die Sammlung leer ist und falsewenn die Sammlung leer ist null.

Ergänzung dazu wird uns geben IsEmptyOrNull.

Sabyasachi Mukherjee
quelle
2

Jon Skeets Antwort ( https://stackoverflow.com/a/28904021/8207463 ) hat einen guten Ansatz unter Verwendung der Erweiterungsmethode - Any () für NULL und EMPTY. ABER er validiert den Fragenbesitzer für den Fall, dass NICHT NULL ist. Ändern Sie daher Jons Ansatz sorgfältig, um AS NULL zu validieren:

If (yourList?.Any() != true) 
{
     ..your code...
}

NICHT verwenden (wird nicht als NULL validiert):

If (yourList?.Any() == false) 
{
     ..your code...
}

Sie können auch für den Fall, dass die Validierung AS NOT NULL (NICHT nur als Beispiel getestet, aber ohne Compilerfehler) Folgendes tun: Prädikat verwenden:

If (yourList?.Any(p => p.anyItem == null) == true) 
{
     ..your code...
}

https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,8788153112b7ffd0

Für welche .NET-Version Sie sie verwenden können, überprüfen Sie bitte:

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any?view=netframework-4.8#moniker-applies-to

Einfach gerecht
quelle
1

Ich hatte das gleiche Problem und löse es wie folgt:

    public bool HasMember(IEnumerable<TEntity> Dataset)
    {
        return Dataset != null && Dataset.Any(c=>c!=null);
    }

"c => c! = null" ignoriert alle Null-Entitäten.

Hosein Djadidi
quelle
1

Ich habe das aus dem gebaut Antwort von @Matt Greer aufgebaut

Er beantwortete die Frage des OP perfekt.

Ich wollte so etwas unter Beibehaltung der ursprünglichen Funktionen von Any und gleichzeitig nach Null suchen. Ich poste dies für den Fall, dass jemand anderes etwas Ähnliches benötigt.

Insbesondere wollte ich immer noch in der Lage sein, ein Prädikat weiterzugeben.

public static class Utilities
{
    /// <summary>
    /// Determines whether a sequence has a value and contains any elements.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of source.</typeparam>
    /// <param name="source">The <see cref="System.Collections.Generic.IEnumerable"/> to check for emptiness.</param>
    /// <returns>true if the source sequence is not null and contains any elements; otherwise, false.</returns>
    public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source)
    {
        return source?.Any() == true;
    }

    /// <summary>
    /// Determines whether a sequence has a value and any element of a sequence satisfies a condition.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of source.</typeparam>
    /// <param name="source">An <see cref="System.Collections.Generic.IEnumerable"/> whose elements to apply the predicate to.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <returns>true if the source sequence is not null and any elements in the source sequence pass the test in the specified predicate; otherwise, false.</returns>
    public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        return source?.Any(predicate) == true;
    }
}

Die Benennung der Erweiterungsmethode könnte wahrscheinlich besser sein.

kb4000
quelle
0

Die andere beste Lösung wie unten, um leer zu prüfen oder nicht?

for(var item in listEnumerable)
{
 var count=item.Length;
  if(count>0)
  {
         // not empty or null
   }
  else
  {
       // empty
  }
}
Shakeer Hussain
quelle
1
Das wird nicht funktionieren, wenn listEnumerablenull ist, was die Frage ist
Timotei
0

Ich benutze dieses:

    public static bool IsNotEmpty(this ICollection elements)
    {
        return elements != null && elements.Count > 0;
    }

Ejem:

List<string> Things = null;
if (Things.IsNotEmpty())
{
    //replaces ->  if (Things != null && Things.Count > 0) 
}
Jhollman
quelle
0

Da einige Ressourcen nach einem Lesevorgang erschöpft sind, dachte ich mir, warum man nicht die Prüfungen und die Lesevorgänge anstelle der herkömmlichen separaten Prüfung kombiniert und dann liest.

Zuerst haben wir eine für die einfachere Check-for-Null-Inline-Erweiterung:

public static System.Collections.Generic.IEnumerable<T> ThrowOnNull<T>(this System.Collections.Generic.IEnumerable<T> source, string paramName = null) => source ?? throw new System.ArgumentNullException(paramName ?? nameof(source));

var first = source.ThrowOnNull().First();

Dann haben wir die etwas kompliziertere Inline-Erweiterung "Check-for-Null-and-Empty" (zumindest so, wie ich es geschrieben habe):

public static System.Collections.Generic.IEnumerable<T> ThrowOnNullOrEmpty<T>(this System.Collections.Generic.IEnumerable<T> source, string paramName = null)
{
  using (var e = source.ThrowOnNull(paramName).GetEnumerator())
  {
    if (!e.MoveNext())
    {
      throw new System.ArgumentException(@"The sequence is empty.", paramName ?? nameof(source));
    }

    do
    {
      yield return e.Current;
    }
    while (e.MoveNext());
  }
}

var first = source.ThrowOnNullOrEmpty().First();

Sie können natürlich weiterhin beide anrufen, ohne die Anrufkette fortzusetzen. Außerdem habe ich den paramName eingefügt, damit der Aufrufer einen alternativen Namen für den Fehler angeben kann, wenn nicht "source" überprüft wird, z. B. "nameof (target)".

rauben
quelle
0
 public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source)
    {
        return source != null && source.Any();
    }

meine eigene Erweiterungsmethode, um Not null und Any zu überprüfen

NobDev
quelle
0

Ohne benutzerdefinierte Helfer empfehle ich entweder ?.Any() ?? falseoder ?.Any() == truedie relativ präzise sind und die Reihenfolge nur einmal angeben müssen.


Wenn ich eine fehlende Sammlung wie eine leere behandeln möchte, verwende ich die folgende Erweiterungsmethode:

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

Diese Funktion kann mit allen LINQ-Methoden kombiniert werden und foreachnicht nur .Any(), weshalb ich sie den spezielleren Hilfsfunktionen vorziehe, die hier vorgeschlagen werden.

CodesInChaos
quelle
0

ich benutze

    list.Where (r=>r.value == value).DefaultIfEmpty().First()

Das Ergebnis ist null, wenn keine Übereinstimmung vorliegt. Andernfalls wird eines der Objekte zurückgegeben

Wenn Sie die Liste wollten, glaube ich, dass das Verlassen von First () oder das Aufrufen von ToList () die Liste oder null liefert.

Ron Chibnik
quelle
0

Schauen Sie sich diese OpenSource-Bibliothek an: Nzr.ToolBox

public static bool IsEmpty(this System.Collections.IEnumerable enumerable)
Mario Santos
quelle
-1

using System.LinqFügen Sie einfach hinzu und sehen Sie, wie die Magie geschieht, wenn Sie versuchen, auf die verfügbaren Methoden in der zuzugreifen IEnumerable. Wenn Sie dies hinzufügen, erhalten Sie Zugriff auf eine Count()so einfache Methode . Denken Sie daran, dies zu überprüfen, null valuebevor Sie anrufen count():)

Mohit
quelle
-1

Ich habe einfach verwendet, um danach zu suchen

Schau dir meine Lösung an

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
Basheer AL-MOMANI
quelle