Modo recomendado para verificar se uma sequência está vazia

Um método retorna uma seqüência, IEnumerable , e agora você quer verificar se está vazio. Como você recomenda fazer isso? Estou procurando boa legibilidade e bom desempenho.

A primeira e mais óbvia maneira é verificar se a contagem é maior que zero:

 if(sequence.Count() == 0) 

Tem legibilidade decente, mas um desempenho terrível, já que tem que passar por toda a sequência.

Um método que às vezes uso é o seguinte:

 if(!sequence.Any()) 

Isso não (até onde eu sei) tem que passar por toda a seqüência, mas a legibilidade é um pouco para trás e desajeitada. (Leia muito melhor se estivermos verificando se a sequência não está vazia).

Outra opção é usar o First em um try-catch, como este:

 try { sequence.First(); } catch(InvalidOperationException) { // Do something } 

Não é uma solução muito bonita, e provavelmente mais lenta também, já que está usando exceções e outras coisas. Poderia evitar isso usando FirstOrDefault , é claro, exceto que você teria um grande problema se o primeiro item na sequência fosse realmente o valor padrão;)

Então, existem outras maneiras de verificar se uma sequência está vazia? Qual você costuma usar? Qual deles você recomenda usar?

Nota: Para melhor legibilidade eu provavelmente colocaria um dos trechos acima em um método de extensão IsEmpty , mas ainda estou curioso, já que eu teria que fazer algo dentro desse método também:

Eu usaria !sequence.Any() , pessoalmente.

Se você realmente precisa, você sempre pode escrever seu próprio método de extensão:

 public static bool IsEmpty(this IEnumerable source) { return !source.Any(); } 

Então você pode escrever:

 if (sequence.IsEmpty()) 

Você pode criar um método de extensão com essa implementação.

 public static bool IsEmpty(this IEnumerable items) { using (var enumerator = items.GetEnumerator()) { return !enumerator.MoveNext(); } } 

Bem, todos esses methods que você está chamando são methods de extensão LINQ, então isso depende de como o provedor LINQ foi implementado. Se você quiser saber se uma sequência está vazia, Count() == 0 ou Any() == false é apropriado. Eu prefiro Any() eu mesmo.

No entanto, dependendo do tipo atual da sequence , talvez não seja necessário usar um método de extensão LINQ. Ou seja, se é um array que você pode chamar de sequence.Length . Se é uma coleção, você pode usar sequence.Count .

Você disse:

if(sequence.Count() == 0) Tem legibilidade decente, mas um desempenho terrível, já que tem que passar por toda a sequência.

Isso é realmente verdade? Você está falando sobre como lidar com uma Interface , IEnumerable , e ainda assim está fazendo suposições a respeito de sua implementação, o que pode ou não ser verdade. Na verdade, muitas das collections personalizadas que escrevi ao longo dos anos mantêm uma variável privada que armazena a contagem atual internamente, o que significa que retornar .Count é um assunto trivial que não exige a repetição de toda a coleção.

Então, com isso dito, a menos que você saiba que uma implementação específica é mal otimizada para .Count , eu usaria .Count . Evite a otimização prematura sempre que possível e mantenha a legibilidade.

Eu uso esses methods de extensão para detectar se a seqüência é nula ou não tem nenhum item e, alternativamente, para detectar se a seqüência tem pelo menos um item, muito parecido com o método string.IsNullOrEmpty() .

  public static bool IsNullOrEmpty(this IEnumerable source) { if (source == null) { return true; } return !source.Any(); } public static bool IsNotNullOrEmpty(this IEnumerable source) { return !source.IsNullOrEmpty(); } . . . if (!sequence.IsNullOrEmpty()) { //Do Something with the sequence... } 

Um método que às vezes uso é o seguinte:

 if(!sequence.Any()) 

Isso não (até onde eu sei) tem que passar por toda a seqüência, mas a legibilidade é um pouco para trás e desajeitada. (Leia muito melhor se estivermos verificando se a sequência não está vazia).

  1. Segundo a Microsoft, Any não precisa passar por toda a sequência. Citando a seção Observações :

A enumeração da source é interrompida assim que o resultado possa ser determinado.

  1. Isso é particularmente verdadeiro ao testar a presença de elementos em uma instrução if-else . Indiscutivelmente, a legibilidade é melhor se testarmos a presença de elementos na instrução if e a ausência de elementos na else , evitando assim o uso do ! operador:

     if (sequence.Any()) { } else { } 

    A maioria consideraria isso mais legível do que:

     if (!sequence.Any()) { } else { }