Как проверить, является ли IEnumerable нулевым или пустым?


155

Я люблю string.IsNullOrEmptyметод. Я хотел бы иметь что-то, что позволило бы такую ​​же функциональность для IEnumerable. Есть ли такие? Может быть, какой-нибудь вспомогательный класс коллекции? Причина, по которой я спрашиваю, состоит в ifтом, что в операторах код выглядит загроможденным, если шаблон имеет значение (mylist != null && mylist.Any()). Было бы намного чище иметь Foo.IsAny(myList).

Этот пост не дает такой ответ: IEnumerable пуст? ,


1
@msarchet: Я бы, наверное, дал вам ответ, если бы это был не комментарий :)
Schultz9999

для меня это похоже на проблему XY. вместо того, чтобы спрашивать «как я могу проверять нулевое значение точно везде, не доставляя ему столько хлопот», вы должны спросить: «Как я могу улучшить свой дизайн, чтобы мне не пришлось проверять наличие нуля везде?»
сара

@nawfal, вопрос, на который вы ссылаетесь, не содержит проверок на
ноль

Ответы:


188

Конечно, вы могли бы написать это:

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

Однако будьте осторожны, что не все последовательности повторяемы; как правило, я предпочитаю ходить только один раз, на всякий случай.


12
Это хороший шаблон? Я бы отбросил this- я считаю, что методы расширения, которые предполагается вызывать, nullявляются признаком уродливого дизайна.
Mormegil

28
@ Mormegil Почему? наконец, методы расширения дают C # некоторую возможность работать с нулями, которые другие языки (например, Ruby) полностью принимают как должное.
Мэтт Грир

5
Почему это обязательно плохо? Как и в этом случае, это иногда очень удобно, поскольку позволяет вам относиться к вещам более однородно и с меньшим количеством особых случаев.
Мистер Замазка

5
@ Mormegil Мех - я не могу быть взволнован об этом. Пока намерения ясны и т. Д.
Марк Гравелл

6
@Miryafa .Any()- это метод расширения, который работает IEnumerable<T>(или IQueryable<T>, хотя это другой сценарий). Это потребляет последовательность , по крайней мере, частично (хотя это по-прежнему означает, что она используется) - может потребоваться прочитать только один элемент (особенно если нет предиката). Таким образом, поскольку sequence ( IEnumerable<T>) не нужно повторять, это может быть так . Any()без предиката по существу эквивалентно foreach(var x in sequence) { return true; } return false;- хотя он использует и GetEnumerator()т. д. вместо синтаксиса компилятора
Марк Гравелл

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

8
ну, не совсем, ОП попросил IEnumerable, а не IEnumerable <T> ;-)
yoyo

8
Да, IEnumerableне имеет Any()расширения.
Блейз

23

Вот модифицированная версия полезного ответа @Matt Greer, которая включает статический класс-обертку, так что вы можете просто скопировать и вставить это в новый исходный файл, не зависит от Linq, и добавляет универсальную IEnumerable<T>перегрузку, чтобы избежать упаковки типов значений это произойдет с неуниверсальной версией. [РЕДАКТИРОВАТЬ: обратите внимание, что использование IEnumerable<T>не предотвращает упаковку перечислителя, это не может предотвратить утка , но, по крайней мере, элементы в коллекции с типизированным значением не будут упакованы.]

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

Другим способом было бы получить Enumerator и вызвать метод MoveNext (), чтобы увидеть, есть ли какие-либо элементы:

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

Это работает как для IEnumerable, так и для IEnumerable <T>.


4
Стоит ли называть распоряжение этим счетчиком? Если коллекция многопоточна в курсе? Да. stackoverflow.com/questions/13459447/…
TamusJRoyce

2
@TamusJRoyce Обратите внимание, что ваше утверждение верно только для IEnumerable<T>, поскольку неуниверсальное IEnumerableне реализуется IDisposable.
Ян Кемп

9

Как я это делаю, используя некоторые современные возможности C #:

Опция 1)

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

Вариант 2)

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

И, между прочим, никогда не используйте Count == 0или Count() == 0просто проверьте, если коллекция пуста. Всегда используйте Linq's.Any()


2
Считать == 0 просто отлично .... Может быть быстрее, чем Any ()? Однако вы правы, считая Count () == 0 плохим. Для тех, кому интересно, Count () перебирает всю вашу коллекцию, поэтому, если она огромная, может добавить кучу накладных расходов!
Энтони Николс

Count () выполняет итерацию только в том случае, если она не может быть приведена к ICollection. Другими словами, когда вы вызываете этот метод, если у объекта уже есть свойство Count, он просто возвращает его, и производительность должна быть одинаковой. Проверьте реализацию здесь: referenceource.microsoft.com/#System.Core/System/Linq/…
Рональд Рей

Если вы работаете с IEnumerable, использование Count () для проверки на пустоту определенно является плохой идеей, поскольку реализация Linq будет перебирать всю коллекцию, тогда как Any просто переместит итератор один раз. Имейте в виду, что в этом случае вы не можете использовать свойство Count, поскольку оно не является частью интерфейса IEnumerable. Вот почему, на мой взгляд, всегда лучше использовать Any () для проверки на пустоту во всех сценариях.
Рональд Рей

Хороший пример того, каким !может быть
Фабио

6

Это может помочь

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

Начиная с C # 6 вы можете использовать нулевое распространение :myList?.Any() == true

Если вы все еще находите это слишком забитым или предпочитаете хороший старый метод расширения, я бы порекомендовал ответы Мэтта Грира и Марка Гравелла, но с расширенной функциональностью для полноты.

Их ответы обеспечивают те же основные функции, но каждый с другой точки зрения. В ответе Мэтта используется string.IsNullOrEmpty-существимость, а в ответе Марка - .Any()путь Линка, чтобы выполнить работу.

Я лично склонен использовать .Any()дорогу, но хотел бы добавить функциональность проверки условий из другой перегрузки метода :

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

Таким образом, вы все еще можете делать что-то вроде: myList.AnyNotNull(item=>item.AnswerToLife == 42);как вы могли бы с обычной, .Any()но с добавленной нулевой проверкой

Обратите внимание, что с помощью C # 6: myList?.Any()возвращает bool?скорее a bool, чем a , что является фактическим эффектом распространения null


1
Проблема с коллекцией? .Any () заключается в том, что это не транзитивно. Если значение равно null, collection? .Any () == true равно false, но collection? .Any () == false также false. Более того,! Collection? .Any () == false тоже ложно ...
Якуб Шулакевич

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

Вот код из ответа Марка Гравелла , а также пример его использования.

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

По его словам, не все последовательности повторяемы, так что код может иногда вызывать проблемы, потому что IsAny()начинается пошаговое выполнение последовательности. Я подозреваю, что имел в виду ответ Роберта Харви, что вам часто не нужно проверять null и опорожнять. Часто вы можете просто проверить на ноль, а затем использовать foreach.

Чтобы не запускать последовательность дважды и воспользоваться преимуществами foreach, я просто написал такой код:

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

Я предполагаю, что метод расширения экономит вам пару строк при наборе текста, но этот код кажется мне более понятным. Я подозреваю, что некоторые разработчики не сразу поймут, что на IsAny(items)самом деле начнут шагать по последовательности. (Конечно, если вы используете много последовательностей, вы быстро научитесь думать, какие шаги через них проходят.)


Если вы позовете IsAny в ноль, он выдаст исключение
Ace Trajkov

3
Вы пробовали это, @Ace? Похоже, это вызовет исключение, но методы расширения могут быть вызваны для нулевых экземпляров .
Дон Киркби

2

я использую Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false); . Надеюсь это поможет.

Сломать:

Collection?.Any()вернется, nullесли Collection имеет значение null, иfalse если Collection пуст.

Collection?.Any()??falseдаст нам, falseесли коллекция пуста, и falseесли коллекцияnull .

Дополнение тому даст нам IsEmptyOrNull.


2

Anwser Джона Скита ( https://stackoverflow.com/a/28904021/8207463 ) имеет хороший подход с использованием метода расширения - Any () для NULL и EMPTY. НО он проверяет владельца вопросов в случае НЕ НУЛЬ. Поэтому тщательно измените подход Джона, чтобы проверить, как NULL:

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

НЕ используйте (не проверяет как NULL):

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

Вы также можете в случае проверки AS NOT NULL (НЕ протестировано в качестве примера, но без ошибки компилятора) сделать что-то вроде использования предиката:

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

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

Для какой версии .NET вы можете его использовать, проверьте:

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


1

У меня была такая же проблема, и я решаю ее так:

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

«c => c! = null» будет игнорировать все нулевые объекты.


1

Я построил это из ответа @Matt Greer

Он отлично ответил на вопрос ОП.

Я хотел что-то подобное, сохраняя при этом исходные возможности Any, а также проверяя на ноль. Я публикую это на тот случай, если кому-то еще понадобится нечто подобное.

В частности, я хотел все еще быть в состоянии передать предикат.

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

Наименование метода расширения может быть лучше.


0

Другое лучшее решение, как ниже, чтобы проверить пусто или нет?

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

1
Это не сработает, если listEnumerableNULL, что вопрос под рукой
Timotei

0

Я использую это:

    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

Поскольку некоторые ресурсы исчерпываются после одного чтения, я подумал, почему бы не объединить проверки и чтения вместо традиционной отдельной проверки, а затем прочитать.

Сначала у нас есть один для более простого встроенного расширения 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();

Тогда нам нужно немного больше (ну, по крайней мере, так, как я написал) встроенное расширение 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();

Конечно, вы можете звонить обоим без продолжения цепочки вызовов. Кроме того, я включил paramName, чтобы вызывающая сторона могла включить альтернативное имя для ошибки, если она не проверяется как «источник», например, «nameof (target)».


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

мой собственный метод расширения, чтобы проверить Не нуль и Любой


0

Без пользовательских помощников я рекомендую либо ?.Any() ?? falseили ?.Any() == trueкоторые относительно краткие и только нужно указать последовательность раз.


Когда я хочу обработать отсутствующую коллекцию как пустую, я использую следующий метод расширения:

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

Эту функцию можно комбинировать со всеми методами LINQ, и foreachне только .Any(), поэтому я предпочитаю ее более специализированным вспомогательным функциям, которые здесь предлагают.


0

я использую

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

Результат будет нулевым, если нет совпадений, в противном случае возвращается один из объектов

Если вы хотите получить список, я полагаю, что выход из First () или вызов ToList () предоставит список или пустое значение.



-1

просто добавьте using System.Linqи увидите магию, происходящую при попытке доступа к доступным методам в IEnumerable. Добавление этого даст вам доступ к методу, названному Count()так просто. просто не забудьте проверить null valueперед звонком count():)


-1

Я использовал простой, если проверить это

проверить мое решение

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.