Як перевірити, чи є IEnumerable нульовим чи порожнім?


154

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

Ця публікація не дає такої відповіді: IEumerable is empty? .


1
@msarchet: Я, мабуть, дам вам відповідь, якби це не коментар :)
Schultz9999

мені здається, це якась проблема XY. замість того, щоб запитувати "як я можу перевірити наявність нуля точно скрізь, не будучи це набридливо", ви повинні запитати "як я можу вдосконалити свій дизайн, щоб я не мав права перевіряти нуль скрізь?"
сара

@nawfal, питання, до якого ви посилаєтесь, не містить спеціально недійсних перевірок, тому я б не вважав це дублікатом
Mygeen

Відповіді:


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 meh - я не можу це хвилювати. Поки наміри ясні і т. Д.
Марк Гравелл

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

119
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>перевантаження, щоб уникнути боксу типів значень що трапиться з негенеричною версією. [EDIT: Зауважте, що використання 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 просто добре .... Можливо, швидше, ніж будь-який ()? Однак ти маєш рацію, що підрахунок () == 0 поганий. Для тих, хто цікавиться, граф () повторює всю вашу колекцію, тому, якщо вона величезна, можна додати тонну накладних витрат!
Ентоні Ніколс

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, що є фактичним ефектом поширення нуля


1
Проблема з колекцією? .Any () в тому, що вона не є перехідною. Коли null, collection? .Any () == true є помилковим, але collection? .Any () == false також 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 на нуль, це викине виняток
Ас Трайков

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

2

я використовую Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false); . Сподіваюся, це допомагає.

Зламатися:

Collection?.Any()повернеться, nullякщо колекція недійсна, а falseякщо колекція порожня.

Collection?.Any()??falseдасть нам, falseякщо колекція порожня, і falseякщо колекція єnull .

Доповнення цього нам дасть IsEmptyOrNull.


2

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

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

НЕ використовуйте (не буде підтверджено AS 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, одночасно перевіряючи на null. Я публікую це у випадку, якщо комусь іншому потрібно щось подібне.

Зокрема, я хотів все-таки передати присудок.

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
Це не спрацює, якщо listEnumerableце недійсне, про що йдеться у справі
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-in-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();
    }

мій власний метод розширення для перевірки Not null та 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 () нададуть цей список або null.



-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.