¿Cómo verificar si IEnumerable es nulo o está vacío?


155

Me encanta el string.IsNullOrEmptymétodo Me encantaría tener algo que permita la misma funcionalidad para IEnumerable. ¿Hay tal? Tal vez alguna clase de ayuda de colección? La razón por la que pregunto es que en las ifdeclaraciones, el código parece desordenado si el patrón lo es (mylist != null && mylist.Any()). Sería mucho más limpio tenerlo Foo.IsAny(myList).

Esta publicación no da esa respuesta: ¿ IEnumerable está vacío? .


1
@msarchet: Probablemente te daría la respuesta si este no fuera el comentario :)
Schultz9999

Para mí, esto parece un problema XY. en lugar de preguntar "¿cómo puedo verificar si hay nulos exactamente en todas partes sin que sea tan molesto?", debe preguntar "¿cómo puedo mejorar mi diseño para no tener que comprobar si hay nulos en todas partes?"
sara

@nawfal, la pregunta a la que se vinculó no incluye controles nulos específicos, por lo que no lo consideraría un duplicado
Mygeen,

Respuestas:


188

Claro que podrías escribir eso:

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

sin embargo, tenga cuidado de que no todas las secuencias sean repetibles; generalmente prefiero caminar solo una vez, por si acaso.


12
¿Es este un buen patrón? Dejaría caer thisallí: considero los métodos de extensión que se supone que se invocan nullcomo un signo de diseño feo.
Mormegil

28
@Mormegil ¿Por qué? Los métodos de extensión finalmente le dan a C # alguna capacidad para trabajar con nulos, que otros lenguajes (como Ruby) dan por completo.
Matt Greer

55
¿Por qué es esto necesariamente malo? Como en este caso, a veces es muy útil ya que le permite tratar las cosas de manera más homogénea y con menos casos especiales.
Sr. Putty

55
@ Mormegil meh: no puedo entusiasmarme con eso. Mientras la intención sea clara, etc.
Marc Gravell

66
@Miryafa .Any()es un método de extensión que funciona IEnumerable<T>(o IQueryable<T>, aunque ese es un escenario diferente). Hacerlo consume la secuencia , al menos parcialmente (aunque eso todavía significa que se consume), es posible que solo necesite leer un elemento (especialmente si no hay un predicado). Como tal, dado que las secuencias ( IEnumerable<T>) no necesitan ser repetibles, eso podría ser . Any()sin un predicado es esencialmente equivalente a foreach(var x in sequence) { return true; } return false;- aunque usa GetEnumerator()etc en lugar de la sintaxis del compilador
Marc Gravell

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

8
bueno, no del todo, el OP pidió IEnumerable, no IEnumerable <T> ;-)
yoyo

8
Sí, IEnumerableno tiene Any()extensión.
Blaise

23

Aquí hay una versión modificada de la útil respuesta de @Matt Greer que incluye una clase de envoltura estática para que pueda copiar y pegar esto en un nuevo archivo fuente, no depende de Linq y agrega una IEnumerable<T>sobrecarga genérica , para evitar el encajonamiento de los tipos de valores eso ocurriría con la versión no genérica. [EDITAR: tenga en cuenta que el uso de IEnumerable<T>no impide el encajonamiento del enumerador, el tipeo de pato no puede evitar eso, pero al menos los elementos en una colección de tipo tipado no estarán encuadrados.]

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

Otra forma sería obtener el enumerador y llamar al método MoveNext () para ver si hay algún elemento:

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

Esto funciona tanto para IEnumerable como para IEnumerable <T>.


44
¿Deberías llamar a dispose en este enumerador? Si la colección es multihilo consciente? Si. stackoverflow.com/questions/13459447/…
TamusJRoyce

2
@TamusJRoyce Tenga en cuenta que su declaración solo es verdadera IEnumerable<T>, ya IEnumerableque no se implementa no genérico IDisposable.
Ian Kemp

9

Como lo hago, aprovechando algunas características modernas de C #:

Opción 1)

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

Opcion 2)

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

Y, por cierto, nunca use Count == 0o Count() == 0solo para verificar si una colección está vacía. Siempre use Linq's.Any()


2
Count == 0 está bien ... ¿Quizás más rápido que Any ()? Sin embargo, tiene razón en que Count () == 0 es malo. Para aquellos que se preguntan, Count () itera a través de toda su colección, por lo que si es enorme podría agregar una tonelada de sobrecarga.
Anthony Nichols

Count () solo itera la enumeración si no se puede convertir a una ICollection. En otras palabras, cuando llama a este método, si ya hay una propiedad Count en el objeto, solo devolverá eso y el rendimiento debería ser idéntico. Vea la implementación aquí: referencesource.microsoft.com/#System.Core/System/Linq/…
Ronald Rey

Si está trabajando con un IEnumerable, usar Count () para probar el vacío es definitivamente una mala idea ya que la implementación de Linq iterará en toda la colección, mientras que Any solo moverá el iterador una vez. Tenga en cuenta que no puede usar la propiedad Count en este caso ya que no es parte de la interfaz IEnumerable. Es por eso que siempre es una mejor idea usar Any () para probar el vacío en todos los escenarios, en mi opinión.
Ronald Rey

Buen ejemplo de cuán no legible !puede ser el operador de negación , especialmente en la segunda opción;)
Fabio

6

Esto puede ayudar

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

Comenzando con C # 6, puede usar propagación nula :myList?.Any() == true

Si todavía encuentra esto demasiado atascado o prefiere un buen método de extensión, recomendaría las respuestas de Matt Greer y Marc Gravell, pero con un poco de funcionalidad extendida para completar.

Sus respuestas proporcionan la misma funcionalidad básica, pero cada una desde otra perspectiva. La respuesta de Matt usa la string.IsNullOrEmpty-mentalidad, mientras que la respuesta de Marc toma el .Any()camino de Linq para hacer el trabajo.

Personalmente, me inclino a usar el .Any()camino, pero me gustaría agregar la funcionalidad de verificación de condición de la otra sobrecarga del método :

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

Por lo tanto, aún puede hacer cosas como: myList.AnyNotNull(item=>item.AnswerToLife == 42);como podría hacerlo .Any()con la verificación nula normal pero agregada

Tenga en cuenta que con la forma C # 6: myList?.Any()devuelve un en bool?lugar de un bool, que es el efecto real de propagar nulo


1
El problema con la colección? .Any () es que no es transitivo. Cuando es nulo, colección? .Any () == verdadero es falso, pero colección? .Any () == falso también es falso. Además
,! Collection

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

Aquí está el código de la respuesta de Marc Gravell , junto con un ejemplo de uso.

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

Como él dice, no todas las secuencias son repetibles, por lo que el código a veces puede causar problemas, porque IsAny()comienza a recorrer la secuencia. Sospecho que la respuesta de Robert Harvey fue que a menudo no es necesario verificar null y vaciar. A menudo, puede verificar si es nulo y luego usarlo foreach.

Para evitar comenzar la secuencia dos veces y aprovechar foreach, acabo de escribir un código como este:

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

Supongo que el método de extensión le ahorra un par de líneas de escritura, pero este código me parece más claro. Sospecho que algunos desarrolladores no se darían cuenta de inmediato de que IsAny(items)realmente comenzarán a recorrer la secuencia. (Por supuesto, si está utilizando muchas secuencias, aprende rápidamente a pensar qué pasos debe seguir).


Si llama a IsAny por nulo, arrojará una excepción
Ace Trajkov el

3
¿Lo intentaste @Ace? Parece que arrojaría una excepción, pero los métodos de extensión se pueden invocar en instancias nulas .
Don Kirkby

2

Yo uso Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false);. Espero que esto ayude.

Descompostura:

Collection?.Any()regresará nullsi la Colección es nula y falsesi la Colección está vacía.

Collection?.Any()??falsenos dará falsesi la Colección está vacía, y falsesi la Colección está vacía null.

Complemento de eso nos dará IsEmptyOrNull.


2

La respuesta de Jon Skeet ( https://stackoverflow.com/a/28904021/8207463 ) tiene un buen enfoque utilizando el Método de extensión - Any () para NULL y EMPTY. PERO está validando el propietario de las preguntas en caso de NO NULL. Así que cambie cuidadosamente el enfoque de Jon para validar AS NULL para:

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

NO use (no validará como NULL):

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

También puede en caso de validar AS NOT NULL (NO probado solo como ejemplo pero sin error del compilador) hacer algo como usar predicado:

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

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

Para qué versión de .NET puede usar, verifique:

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


1

Tuve el mismo problema y lo resuelvo como:

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

"c => c! = null" ignorará todas las entidades nulas.


1

Construí esto a partir de la respuesta de @Matt Greer

Él respondió la pregunta del OP perfectamente.

Quería algo como esto mientras mantenía las capacidades originales de Any mientras también comprobaba null. Estoy publicando esto en caso de que alguien más necesite algo similar.

Específicamente, todavía quería poder pasar un predicado.

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

El nombramiento del método de extensión probablemente podría ser mejor.


0

¿La otra mejor solución como la siguiente para marcar vacío o no?

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

1
Eso no funcionará si listEnumerablees nulo, que es la pregunta en cuestión
Timotei

0

Yo uso este:

    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

Dado que algunos recursos se agotan después de una lectura, pensé por qué no combinar los cheques y las lecturas, en lugar del cheque separado tradicional, luego leer.

Primero tenemos uno para la extensión en línea más simple 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();

Luego tenemos el poco más involucrado (bueno, al menos la forma en que lo escribí) extensión en línea 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();

Por supuesto, aún puede llamar a ambos sin continuar la cadena de llamadas. Además, incluí el paramName, de modo que la persona que llama puede incluir un nombre alternativo para el error si no se está comprobando "fuente", por ejemplo, "nombrede (destino)".


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

mi propio método de extensión para marcar No nulo y Cualquiera


0

Sin ayudantes personalizados recomiendo ya sea ?.Any() ?? falseo ?.Any() == truelo que es necesidad relativamente concisa y sólo para especificar la secuencia de una vez.


Cuando quiero tratar una colección faltante como una colección vacía, utilizo el siguiente método de extensión:

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

Esta función se puede combinar con todos los métodos LINQ y foreach, no solo .Any(), es por eso que la prefiero a las funciones auxiliares más especializadas que la gente propone aquí.


0

yo suelo

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

El resultado será nulo si no coincide, de lo contrario devuelve uno de los objetos

Si quería la lista, creo que dejar First () o llamar a ToList () proporcionará la lista o será nula.



-1

simplemente agregue using System.Linqy vea la magia que ocurre cuando intenta acceder a los métodos disponibles en el IEnumerable. Agregar esto le dará acceso al método nombrado Count()tan simple como eso. solo recuerde verificar null valueantes de llamar count():)


-1

Solía ​​simple si para verificarlo

mira mi solución

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.