Como verificar se IEnumerable é nulo ou vazio?


154

Eu amo string.IsNullOrEmptymétodo. Eu adoraria ter algo que permita a mesma funcionalidade para IEnumerable. Existe tal? Talvez alguma classe auxiliar de coleta? A razão pela qual estou perguntando é que, nas ifdeclarações, o código parece confuso se o padrão é (mylist != null && mylist.Any()). Seria muito mais limpo ter Foo.IsAny(myList).

Esta publicação não fornece a resposta: IEnumerable está vazio? .


1
@msarchet: Eu provavelmente dar-lhe a resposta se não fosse este o comentário :)
Schultz9999

para mim, isso parece um tipo de problema XY. em vez de perguntar "como posso verificar se há nulo exatamente em todos os lugares sem que seja tão incômodo", você deve perguntar "como posso melhorar meu design para não ter que verificar se há nulo em todos os lugares?"
Sara #

@nawfal, a pergunta que você vinculou não inclui verificações especificamente nulas, por isso não consideraria uma duplicata
Mygeen

Respostas:


188

Claro que você poderia escrever isso:

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

no entanto, tenha cuidado para que nem todas as sequências sejam repetíveis; geralmente eu prefiro andar com eles apenas uma vez, só por precaução.


12
Esse é um bom padrão? Gostaria de largar o thislá - considero métodos de extensão que se supõe serem chamados nullcomo um sinal de design feio.
Mormegil

28
@Mormegil Why? os métodos de extensão finalmente fornecem ao C # alguma capacidade de trabalhar com valores nulos, que outros idiomas (como Ruby) consideram completamente garantidos.
Matt Greer

5
Por que isso é necessariamente ruim? Como neste caso, às vezes é muito útil, pois permite tratar as coisas de maneira mais homogênea e com menos casos especiais.
Mr. Putty

5
@Mormegil meh - eu não posso ficar animado com isso. Contanto que a intenção seja clara, etc.
Marc Gravell

6
@Miryafa .Any()é um método de extensão que opera IEnumerable<T>(ou IQueryable<T>, embora esse seja um cenário diferente). Fazer isso consome a sequência , pelo menos parcialmente (embora isso ainda signifique que é consumido) - talvez seja necessário ler apenas um elemento (especialmente se não houver predicado). Como tal, como sequences ( IEnumerable<T>) não precisa ser repetível, pode ser isso . Any()sem um predicado é essencialmente equivalente a foreach(var x in sequence) { return true; } return false;- embora utilize GetEnumerator()etc, em vez de sintaxe compilador
Marc Gravell

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

8
Bem, não exatamente, o OP pediu IEnumerable, não IEnumerable <T> ;-)
yoyo

8
Sim, IEnumerablenão tem Any()extensão.
Blaise

23

Aqui está uma versão modificada da resposta útil do @Matt Greer, que inclui uma classe de empacotador estático, para que você possa copiar e colar isso em um novo arquivo de origem, não depender do Linq e adicionar uma IEnumerable<T>sobrecarga genérica , para evitar o encaixe de tipos de valor isso ocorreria com a versão não genérica. [EDIT: Observe que o uso de IEnumerable<T>não impede o encaixotamento do enumerador, a digitação com patos não pode impedir isso, mas pelo menos os elementos em uma coleção com valor de tipo não serão encaixotados.]

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

Outra maneira seria obter o Enumerator e chamar o método MoveNext () para ver se há algum item:

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

Isso funciona para IEnumerable e IEnumerable <T>.


4
Você deve ligar para dispor neste enumerador? Se a coleção está ciente de multithreading? Sim. stackoverflow.com/questions/13459447/…
TamusJRoyce 10/15

2
@TamusJRoyce Note-se que a sua declaração só é verdade para IEnumerable<T>, como não genérico IEnumerablenão implementa IDisposable.
22616 Ian Kemp

9

Do jeito que eu faço isso, aproveitando alguns recursos modernos do C #:

Opção 1)

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

Opção 2)

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

E, a propósito, nunca use Count == 0ou Count() == 0apenas para verificar se uma coleção está vazia. Sempre use Linq's.Any()


2
Contagem == 0 está bem .... Talvez mais rápido que Qualquer ()? No entanto, você está correto no fato de Count () == 0 ser ruim. Para aqueles que estão se perguntando, o Count () percorre toda a sua coleção, por isso, se for enorme, poderá adicionar uma tonelada de sobrecarga!
Anthony Nichols

Count () apenas itera a enumeração se não puder ser convertida em uma ICollection. Em outras palavras, quando você chama esse método, se já houver uma propriedade Count no objeto, ele retornará isso e o desempenho deverá ser idêntico. Confira a implementação aqui: referencesource.microsoft.com/#System.Core/System/Linq/…
Ronald Rey

Se você estiver trabalhando com um IEnumerable, usar Count () para testar o vazio é definitivamente uma péssima idéia, pois a implementação do Linq iterará por toda a coleção, enquanto Any apenas moverá o iterador uma vez. Lembre-se de que você não pode usar a propriedade Count neste caso, pois ela não faz parte da interface IEnumerable. É por isso que sempre é uma ideia melhor usar Any () para testar o vazio em todos os cenários, na minha opinião.
Ronald Rey

Bom exemplo de operador de negação como não legível !pode ser, especialmente na segunda opção;)
Fabio

6

Isso pode ajudar

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

A partir do C # 6, você pode usar a propagação nula :myList?.Any() == true

Se você ainda achar isso muito entupido ou preferir um bom e velho método de extensão, eu recomendaria as respostas de Matt Greer e Marc Gravell, mas com um pouco de funcionalidade estendida para completar.

Suas respostas fornecem a mesma funcionalidade básica, mas cada uma de outra perspectiva. A resposta de Matt usa a string.IsNullOrEmptymentalidade, enquanto a resposta de Marc segue o .Any()caminho de Linq para fazer o trabalho.

Pessoalmente, estou inclinado a usar a .Any()estrada, mas gostaria de adicionar a funcionalidade de verificação de condição da outra sobrecarga do 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);
    }

Então você ainda pode fazer coisas como: myList.AnyNotNull(item=>item.AnswerToLife == 42);como você poderia fazer com a .Any()verificação nula regular, mas com a adição nula

Observe que, com a maneira C # 6: myList?.Any()retorna a em bool?vez de a bool, que é o efeito real da propagação nula


1
O problema com a coleção? .Any () é que não é transitivo. Quando nulo, a coleção? .Any () == true é falsa, mas a coleção? .Any () == false também é falsa. Além disso,! Collection? .Any () == false também é false ...
Jakub Szułakiewicz 28/01

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

Aqui está o código da resposta de Marc Gravell , junto com um exemplo de como usá-lo.

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 ele diz, nem todas as seqüências são repetíveis, de modo que o código às vezes pode causar problemas, porque IsAny()começa a percorrer a sequência. Suspeito que a resposta de Robert Harvey significou que muitas vezes você não precisa procurar null e esvaziar. Freqüentemente, você pode simplesmente verificar se há nulo e depois usá-lo foreach.

Para evitar iniciar a sequência duas vezes e aproveitar foreach, acabei de escrever um 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.");
        }
    }
}

Eu acho que o método de extensão poupa algumas linhas de digitação, mas esse código parece mais claro para mim. Suspeito que alguns desenvolvedores não percebam imediatamente que IsAny(items)realmente começarão a percorrer a sequência. (Obviamente, se você estiver usando muitas seqüências, aprende rapidamente a pensar em quais etapas são necessárias.)


Se você chamar IsAny em um nulo, ele irá lançar uma exceção
Ace Trajkov

3
Você tentou, @Ace? Parece que isso geraria uma exceção, mas os métodos de extensão podem ser chamados em instâncias nulas .
Don Kirkby

2

Eu uso Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false);. Espero que isto ajude.

Demolir:

Collection?.Any()retornará nullse a coleção for nula e falsese a coleção estiver vazia.

Collection?.Any()??falsenos fornecerá falsese a coleção estiver vazia e falsese a coleção estiver null.

Complemento disso nos dará IsEmptyOrNull.


2

O respondente de Jon Skeet ( https://stackoverflow.com/a/28904021/8207463 ) tem uma boa abordagem usando o Extension Method - Any () para NULL e EMPTY. MAS ele está validando o proprietário das perguntas no caso de NOT NULL. Portanto, mude cuidadosamente a abordagem de Jon para validar AS NULL para:

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

NÃO use (não validará como nulo):

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

Você também pode, em caso validar AS NOT NULL (não testado apenas como exemplo, mas sem erro do compilador) fazer algo assim usando predicado:

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

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

Para qual versão do .NET você pode usá-lo, verifique:

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


1

Eu tive o mesmo problema e resolvo-o como:

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

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


1

Eu construí esta off da resposta até @ Matt Greer

Ele respondeu à pergunta do OP perfeitamente.

Eu queria algo assim, mantendo os recursos originais de Any e também verificando se era nulo. Estou postando isso no caso de alguém precisar de algo semelhante.

Especificamente, eu ainda queria passar um 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;
    }
}

A nomeação do método de extensão provavelmente poderia ser melhor.


0

A outra melhor solução como abaixo para verificar vazio ou não?

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

1
Isso não vai funcionar, se listEnumerableé nulo, que é a questão em apreço
Timotei

0

Eu 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

Como alguns recursos se esgotam após uma leitura, pensei por que não combinar as verificações e as leituras, em vez da verificação separada tradicional, e depois ler.

Primeiro, temos um para a extensão inline check-for-null mais simples:

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

Em seguida, temos o pouco mais envolvido (bem, pelo menos do jeito que escrevi), verifique se há uma extensão inline vazia e nula:

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

É claro que você ainda pode ligar para os dois sem continuar a cadeia de chamadas. Além disso, incluí o paramName, para que o responsável pela chamada possa incluir um nome alternativo para o erro, se a "origem" não estiver sendo verificada, por exemplo, "nameof (target)".


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

meu próprio método de extensão para verificar Não nulo e Qualquer


0

Sem auxiliares personalizados, eu recomendo ?.Any() ?? falseou ?.Any() == trueque são relativamente concisos e precisam especificar a sequência apenas uma vez.


Quando quero tratar uma coleção ausente como uma coleção vazia, uso o seguinte método de extensão:

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

Essa função pode ser combinada com todos os métodos LINQ e foreach, não apenas .Any(), é por isso que eu a prefiro sobre as funções auxiliares mais especializadas que as pessoas estão propondo aqui.


0

eu uso

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

O resultado será nulo se não houver correspondência, caso contrário, retorna um dos objetos

Se você quisesse a lista, acredito que sair de First () ou chamar ToList () fornecerá a lista ou null.



-1

basta adicionar using System.Linqe ver a mágica acontecendo quando você tenta acessar os métodos disponíveis no IEnumerable. Adicionando isso, você terá acesso ao método nomeado Count()tão simples quanto isso. lembre-se de verificar null valueantes de ligar count():)


-1

Eu usei simples se verificar

confira minha solução

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.