Jak sprawdzić, czy IEnumerable ma wartość NULL lub jest pusty?


154

Uwielbiam string.IsNullOrEmptymetodę. Chciałbym mieć coś, co zapewniłoby tę samą funkcjonalność dla IEnumerable. Czy jest taki? Może jakaś klasa pomocnika kolekcji? Powodem, o który pytam, jest to, że w ifinstrukcjach kod wygląda na zagracony, jeśli wzór jest (mylist != null && mylist.Any()). Byłoby o wiele czystsze Foo.IsAny(myList).

Ten post nie daje takiej odpowiedzi: IEnumerable jest puste? .


1
@msarchet: Prawdopodobnie dałbym ci odpowiedź, gdyby to nie był komentarz :)
Schultz9999

wydaje mi się, że to rodzaj problemu z XY. zamiast pytać „jak mogę sprawdzić null dokładnie wszędzie, tak by nie było to tak uciążliwe”, należy zapytać „jak mogę ulepszyć mój projekt, aby nie MUSZĘ sprawdzać, czy jest null wszędzie?”
sara

@nawfal, pytanie, do którego utworzyłeś link, nie zawiera konkretnie zerowych sprawdzeń, więc nie
uważałbym

Odpowiedzi:


188

Jasne, że mógłbyś napisać:

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

należy jednak uważać, aby nie wszystkie sekwencje były powtarzalne; generalnie wolę spacerować tylko raz, na wszelki wypadek.


12
Czy to dobry wzór? Rzuciłbym to this- uważam metody rozszerzające, które są przypuszczalnie wywoływane nulljako oznaka brzydkiego projektu.
Mormegil,

28
@Mormegil Dlaczego? metody rozszerzające ostatecznie dają C # pewne możliwości pracy z wartościami null, które inne języki (takie jak Ruby) przyjmują za pewnik.
Matt Greer,

5
Dlaczego jest to koniecznie złe? Podobnie jak w tym przypadku, jest to czasami bardzo przydatne, ponieważ pozwala traktować rzeczy bardziej jednorodnie i z mniejszą liczbą przypadków specjalnych.
Mr. Putty

5
@Mormegil meh - nie mogę się tym ekscytować. O ile zamiar jest jasny, itd.
Marc Gravell

6
@Miryafa .Any()to metoda rozszerzająca, która działa na IEnumerable<T>(lub IQueryable<T>, chociaż to inny scenariusz). Robi to , przynajmniej częściowo, zużywa sekwencję (chociaż nadal oznacza to, że jest zużyta) - może być konieczne odczytanie tylko jednego elementu (zwłaszcza jeśli nie ma predykatu). Jako takie, ponieważ sekwencje ( IEnumerable<T>) nie muszą być powtarzalne, to może być to . Any()bez predykatu jest zasadniczo równoważne foreach(var x in sequence) { return true; } return false;- chociaż używa GetEnumerator()etc zamiast składni kompilatora
Marc Gravell

119
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) {
    return enumerable == null || !enumerable.Any();
}

8
no, nie do końca, PO poprosił o IEnumerable, a nie IEnumerable <T> ;-)
yoyo

8
Tak, IEnumerablenie ma Any()rozszerzenia.
Blaise

23

Oto zmodyfikowana wersja przydatnej odpowiedzi @Matt Greer, która zawiera statyczną klasę opakowującą, więc możesz ją po prostu skopiować i wkleić do nowego pliku źródłowego, nie zależy od Linq i dodaje ogólne IEnumerable<T>przeciążenie, aby uniknąć pakowania typów wartości co by się zdarzyło w przypadku wersji innej niż ogólna. [EDYTUJ: Zauważ, że użycie IEnumerable<T>nie zapobiega pudełkowaniu modułu wyliczającego, pisanie kaczkami nie może temu zapobiec, ale przynajmniej elementy w kolekcji o typie wartości nie będą każdy z nich.]

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

15

Innym sposobem byłoby pobranie modułu Enumerator i wywołanie metody MoveNext (), aby sprawdzić, czy są jakieś elementy:

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

Działa to zarówno dla IEnumerable, jak i IEnumerable <T>.


4
Czy powinieneś zadzwonić do dyspozycji tego modułu wyliczającego? Jeśli kolekcja obsługuje wielowątkowość? Tak. stackoverflow.com/questions/13459447/…
TamusJRoyce

2
@TamusJRoyce Zauważ, że twoje stwierdzenie jest prawdziwe tylko dla IEnumerable<T>, ponieważ nieogólne IEnumerablenie implementuje IDisposable.
Ian Kemp

9

Sposób, w jaki to robię, wykorzystując kilka nowoczesnych funkcji C #:

Opcja 1)

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

Opcja 2)

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

A tak przy okazji, nigdy nie używaj Count == 0lub Count() == 0po prostu sprawdzaj, czy kolekcja jest pusta. Zawsze używaj Linq's.Any()


2
Count == 0 jest w porządku .... Może szybciej niż Any ()? Jednak masz rację, stwierdzając, że Count () == 0 jest zły. Dla tych, którzy się zastanawiają, Count () iteruje przez całą Twoją kolekcję, więc jeśli jest ogromna, może dodać mnóstwo narzutów!
Anthony Nichols

Count () wykonuje iterację wyliczenia tylko wtedy, gdy nie można go rzutować na ICollection. Innymi słowy, jeśli wywołasz tę metodę, jeśli obiekt ma już właściwość Count, zwróci ją, a wydajność powinna być identyczna. Sprawdź implementację tutaj: referenceource.microsoft.com/#System.Core/System/Linq/ ...
Ronald Rey

Jeśli pracujesz z IEnumerable, użycie Count () do testowania pustości jest zdecydowanie złym pomysłem, ponieważ implementacja Linq BĘDZIE iterować po całej kolekcji, podczas gdy Any po prostu przeniesie iterator raz. Należy pamiętać, że w tym przypadku nie można użyć właściwości Count, ponieważ nie jest ona częścią interfejsu IEnumerable. Dlatego moim zdaniem zawsze lepszym pomysłem jest użycie Any () do przetestowania pustości we wszystkich scenariuszach.
Ronald Rey

Niezły przykład tego, jak nieczytelny !może być operator negacji , szczególnie w drugiej opcji;)
Fabio

6

To może pomóc

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

5

Począwszy od C # 6 możesz użyć propagacji wartości null :myList?.Any() == true

Jeśli nadal uważasz, że jest to zbyt zatkane lub wolisz starą dobrą metodę rozszerzenia, polecam odpowiedzi Matta Greera i Marca Gravella, ale z odrobiną rozszerzonej funkcjonalności dla kompletności.

Ich odpowiedzi zapewniają tę samą podstawową funkcjonalność, ale każda z innej perspektywy. Odpowiedź Matta wykorzystuje string.IsNullOrEmptymentalność, podczas gdy odpowiedź Marca prowadzi Linqa .Any()do wykonania zadania.

Osobiście jestem skłonny do korzystania z .Any()drogi, ale chciałbym dodać funkcję sprawdzania stanu z innego przeciążenia metody :

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

Więc nadal możesz robić takie rzeczy, jak: myList.AnyNotNull(item=>item.AnswerToLife == 42);tak jak w przypadku zwykłego, .Any()ale z dodanym sprawdzaniem zerowym

Zwróć uwagę, że w C # 6 sposób: myList?.Any()zwraca a bool?zamiast a bool, co jest rzeczywistym efektem propagacji null


1
Problem z kolekcjonowaniem? .Any () jest taki, że nie jest przechodni. Kiedy null, collection? .Any () == true jest false, ale collection? .Any () == false jest również false. Ponadto! Kolekcja? .Any () == false też jest fałszywe ...
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
}

2

Oto kod z odpowiedzi Marca Gravella wraz z przykładem jego użycia.

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.");
        }
    }
}

Jak mówi, nie wszystkie sekwencje są powtarzalne, więc kod może czasami powodować problemy, ponieważ IsAny()zaczyna przechodzić przez sekwencję. Podejrzewam, że odpowiedź Roberta Harveya oznaczała, że ​​często nie trzeba sprawdzać null i opróżniać. Często wystarczy sprawdzić wartość null, a następnie użyć foreach.

Aby uniknąć dwukrotnego uruchamiania sekwencji i skorzystać z tego foreach, po prostu napisałem kod podobny do tego:

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.");
        }
    }
}

Wydaje mi się, że metoda rozszerzenia pozwala zaoszczędzić kilka wierszy pisania, ale ten kod wydaje mi się bardziej przejrzysty. Podejrzewam, że niektórzy programiści nie od razu zorientują się, że IsAny(items)faktycznie zaczną przechodzić przez sekwencję. (Oczywiście, jeśli używasz wielu sekwencji, szybko nauczysz się myśleć o tym, co przez nie przechodzi).


Jeśli sprawdzisz IsAny na null, wyrzuci wyjątek
Ace Trajkov

3
Próbowałeś tego, @Ace? Wygląda na to, że zgłosiłby wyjątek, ale metody rozszerzające można wywoływać w wystąpieniach o wartości null .
Don Kirkby

2

używam Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false); . Mam nadzieję że to pomoże.

Awaria:

Collection?.Any()zwróci, nulljeśli Collection jest null i falsejeśli Collection jest pusta.

Collection?.Any()??falsepoda nam, falseczy kolekcja jest pusta, a falsejeśli kolekcja jestnull .

Uzupełnienie tego da nam IsEmptyOrNull.


2

Anwser Jona Skeeta ( https://stackoverflow.com/a/28904021/8207463 ) ma dobre podejście przy użyciu metody rozszerzenia - Any () dla NULL i EMPTY. ALE zatwierdza właściciela pytań w przypadku NIE NULL. Dlatego ostrożnie zmień podejście Jona do walidacji AS NULL na:

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

NIE używaj (nie zweryfikuje JAKO NULL):

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

Możesz również w przypadku walidacji jako NIE NULL (NIE testowane jako przykład, ale bez błędu kompilatora) zrobić coś takiego, jak użycie predykatu:

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

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

Dla której wersji .NET możesz go używać, sprawdź:

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


1

Miałem ten sam problem i rozwiązuję go tak:

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

„c => c! = null” zignoruje wszystkie puste jednostki.


1

Zbudowałem to na podstawie odpowiedzi @Matt Greer

Doskonale odpowiedział na pytanie OP.

Chciałem coś takiego, zachowując oryginalne możliwości Any, jednocześnie sprawdzając, czy nie ma wartości null. Publikuję to na wypadek, gdyby ktoś inny potrzebował czegoś podobnego.

W szczególności chciałem nadal móc przekazać predykat.

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

Nazewnictwo metody rozszerzenia mogłoby być prawdopodobnie lepsze.


0

Inne najlepsze rozwiązanie, jak poniżej, aby sprawdzić, czy jest puste, czy nie?

for(var item in listEnumerable)
{
 var count=item.Length;
  if(count>0)
  {
         // not empty or null
   }
  else
  {
       // empty
  }
}

1
To nie zadziała, jeśli listEnumerablejest zerowe, co jest pytaniem pod ręką
Timotei,

0

Używam tego:

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

0

Ponieważ niektóre zasoby wyczerpują się po jednym czytaniu, pomyślałem, dlaczego nie połączyć sprawdzeń i odczytów zamiast tradycyjnego oddzielnego sprawdzenia, a następnie przeczytać.

Najpierw mamy jedno dla prostszego rozszerzenia wbudowanego check-for-null:

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();

Następnie mamy trochę bardziej zaangażowane (cóż, przynajmniej tak, jak to napisałem) rozszerzenie wbudowane check-for-null-and-empty:

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();

Oczywiście możesz nadal dzwonić do obu bez kontynuowania łańcucha połączeń. Dołączyłem również parametr paramName, aby wywołujący mógł podać alternatywną nazwę błędu, jeśli nie jest sprawdzane „źródło”, np. „Nameof (target)”.


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

moja własna metoda rozszerzenia, aby sprawdzić Not null i Any


0

Bez niestandardowych pomocników polecam albo ?.Any() ?? falselub ?.Any() == truektóre są stosunkowo zwięzły i tylko trzeba określić sekwencję raz.


Kiedy chcę traktować brakującą kolekcję jak pustą, używam następującej metody rozszerzenia:

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

Tę funkcję można łączyć ze wszystkimi metodami LINQ i foreachnie tylko .Any(), dlatego wolę ją od bardziej wyspecjalizowanych funkcji pomocniczych, które ludzie tutaj proponują.


0

używam

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

Wynik będzie pusty, jeśli brak dopasowania, w przeciwnym razie zwraca jeden z obiektów

Jeśli chcesz mieć listę, myślę, że opuszczenie First () lub wywołanie ToList () dostarczy listę lub wartość null.



-1

po prostu dodaj using System.Linqi zobacz magię, która dzieje się, gdy próbujesz uzyskać dostęp do dostępnych metod w IEnumerable. Dodanie tego da ci dostęp do metody o Count()tak prostej nazwie . pamiętaj tylko, aby sprawdzić null valueprzed zadzwonieniem count():)


-1

Użyłem prostego, jeśli to sprawdzić

sprawdź moje rozwiązanie

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.