C#是否具有IsNullOrEmpty用于List / IEnumerable?


68

我知道通常空列表比NULL更可取。但是我将返回NULL,主要有两个原因

  1. 我必须显式检查并处理null值,以避免bug和攻击。
  2. ??之后很容易执行操作以获得返回值。

对于字符串,我们有IsNullOrEmpty。有什么从C#本身做了列表或IEnumerable的同样的事情?


7
否,但是添加扩展方法很简单。
亚伦·麦克弗

Answers:


72

框架中没有任何东西,但这是一个非常简单的扩展方法。

看这里

/// <summary>
    /// Determines whether the collection is null or contains no elements.
    /// </summary>
    /// <typeparam name="T">The IEnumerable type.</typeparam>
    /// <param name="enumerable">The enumerable, which may be null or empty.</param>
    /// <returns>
    ///     <c>true</c> if the IEnumerable is null or empty; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
    {
        if (enumerable == null)
        {
            return true;
        }
        /* If this is a list, use the Count property for efficiency. 
         * The Count property is O(1) while IEnumerable.Count() is O(N). */
        var collection = enumerable as ICollection<T>;
        if (collection != null)
        {
            return collection.Count < 1;
        }
        return !enumerable.Any(); 
    }

出于性能原因,Daniel Vaughan采取了额外的步骤(可能的话)转换为ICollection。我不会想到的事情。


5
尽管必须谨慎,但对的调用enumerable.Any()可能会丢失不可倒转枚举的元素。您可以将其包装在可以跟踪第一个元素的东西中(当然可以很好地处理null,否则我们会否定OP的整个问题),但是在某些情况下将其与空枚举合并可以更方便一些,只使用结果即可。
乔恩·汉娜

1
是否也应该尝试强制转换为非泛型ICollection,因为anIEnumerable<Animal>可能类似于的实例List<Cat>,该实例没有实现ICollection<Animal>但确实实现了非泛型ICollection
2012年

7
如此处所示:msdn.microsoft.com/en-us/library/bb337697%28v=vs.110%29.aspx(复选标记)“可以确定结果后立即停止源枚举。” 这让我觉得没有必要强制转换为collection.Any()也应该具有O(1)。那么,如果您真的不知道它是列表,为什么还要转换为列表呢?
xmoex

3
现在可以将其写成一行:return a == null || ((a为ICollection)?. Count == 0)|| !a.Any();
PRMan

2
@ElMac ICollection.Count的规范是对计数进行跟踪,任何实现都应返回计数,而无需迭代值以根据需要进行计算。但是正如另一个人在评论中所说,也应该为IReadOnlyCollection调用相同的优化。此外,LINQ.Any在ICollection上使用优化,但不在IReadOnlyCollection上使用。

50

后期更新:从C#6.0开始,可以使用null传播运算符来表示简洁,如下所示:

if (  list?.Count  > 0 ) // For List<T>
if ( array?.Length > 0 ) // For Array<T>

或者,作为更清洁,更通用的替代品IEnumerable<T>

if ( enumerable?.Any() ?? false )

注1:IsNotNullOrEmpty与OP问题(引用)相比,所有较高的变体实际上都反映出来:

由于运算符优先级,因此IsNullOrEmpty等效项看起来不太吸引人:
if (!(list?.Count > 0))

注意2: ?? false是必要的,因为以下原因(此帖子的摘要/引用):

?.null如果子成员为,运算符将返回null。但是,如果我们尝试让非Nullable成员(如 Any()方法)返回bool[...],则编译器将“包装”返回值Nullable<>。例如,Object?.Any()将给我们bool?(是Nullable<bool>),而不是bool。[...]由于无法将其隐式转换bool为该表达式,因此无法在if

注意3:作为奖励,该语句也是“线程安全的”(引用此问题的答案):

在多线程上下文中,如果[枚举]可从另一个线程访问(或者因为它是可访问的字段,或者因为它在暴露给另一个线程的lambda中关闭了),则每次计算时该值可能都不同[ ieprior null -检查]


@ GrimR3是的,您是对的:这个问题更加棘手。我这样写是为了使其与引用/引荐保持一致。
Teodor Tite

您可以使用以下方法检查0或1个元素:(myArray?.Length ?? 0) < 2
intrepidis

24

没有内置任何内容。

但是,这是一个简单的扩展方法:

public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
  if(enumerable == null)
    return true;

  return !enumerable.Any();
}

1
我不知道为什么这个和被接受的答案都错过了!在Any()调用之前。该错误已传播到我团队的源代码中。
stannius '16


3

将前面的答案放到C#6.0+的简单扩展方法中:

    public static bool IsNullOrEmpty<T>(this IEnumerable<T> me) => !me?.Any() ?? true;

1

如果在不为空的情况下需要能够检索所有元素,则此处的某些答案将不起作用,因为Any()对不可重绕的枚举的调用将“忘记”一个元素。

您可以采用另一种方法,将空值转换为空值:

bool didSomething = false;
foreach(var element in someEnumeration ?? Enumerable.Empty<MyType>())
{
  //some sensible thing to do on element...
  didSomething = true;
}
if(!didSomething)
{
  //handle the fact that it was null or empty (without caring which).
}

同样(someEnumeration ?? Enumerable.Empty<MyType>()).ToList()可以使用等。


您能否举一个.NET中不可撤消集合的示例?我的意思是只是框架中现有类的名称,而不是要求您给我一个示例实现的例子。
AaronLS 2013年

@AaronLS是大多数Linq查询的结果。
乔恩·汉娜

在这种情况下,具体类型是什么?我知道IQueryable并不是一个真正的列表,除非您尝试ToList / foreach在这一点实现。我想象IQueryable有一些中间的具体类型,它是幕后唯一的向前数据读取器,所以您所说的很有意义,我只是无法想象您实际上将在哪里遇到此问题。如果foreach(var item in someQueryable)生成自己的枚举数,然后someQueryable.Any()进行单独的查询,则不会影响当前的foreach枚举数AFAIK。
AaronLS

不挑战您,只是想解释我的困惑。
AaronLS

@AaronLS它将是一个单独的枚举器,这将要求您再次运行查询,这已经够糟糕了。如果将其强制转换为,IEnumerable则更是如此,因为查询将丢失。
乔恩·汉纳

1

就像其他人都说的那样,框架没有任何内容,但是如果您使用Castle,那么Castle.Core.Internal就会拥有它。

using Castle.Core.Internal;

namespace PhoneNumbers
{
    public class PhoneNumberService : IPhoneNumberService
    {
        public void ConsolidateNumbers(Account accountRequest)
        {
            if (accountRequest.Addresses.IsNullOrEmpty()) // Addresses is List<T>
            {
                return;
            }
            ...

0

我修改了Matthew Vines的建议,以避免出现“可能的IEnumerable多重枚举”的问题。(另请参阅乔恩·汉纳的评论)

public static bool IsNullOrEmpty(this IEnumerable items)
    => items == null
    || (items as ICollection)?.Count == 0
    || !items.GetEnumerator().MoveNext();

...以及单元测试:

[Test]
public void TestEnumerableEx()
{
    List<int> list = null;
    Assert.IsTrue(list.IsNullOrEmpty());

    list = new List<int>();
    Assert.IsTrue(list.IsNullOrEmpty());

    list.AddRange(new []{1, 2, 3});
    Assert.IsFalse(list.IsNullOrEmpty());

    var enumerator = list.GetEnumerator();
    for(var i = 1; i <= list.Count; i++)
    {
        Assert.IsFalse(list.IsNullOrEmpty());
        Assert.IsTrue(enumerator.MoveNext());
        Assert.AreEqual(i, enumerator.Current);
    }

    Assert.IsFalse(list.IsNullOrEmpty());
    Assert.IsFalse(enumerator.MoveNext());
}

0
var nullOrEmpty = !( list?.Count > 0 );

2
仅代码答案不是好的答案,请尝试添加几行代码来说明问题所在以及您的代码如何解决
MikeT

如果list是IEnumerable,则不会编译。
Lee Grissom

0

对我来说最好的isNullOrEmpty方法看起来像这样

public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
    return !enumerable?.Any() ?? true;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.