如何检查IEnumerable是否为空或为空?

211
我喜欢 string.IsNullOrEmpty 方法。我希望有一个类似的方法可以用于 IEnumerable。是否有这样的方法?也许有一些集合辅助类?我之所以问这个问题是因为在 if 语句中,如果模式是 (mylist != null && mylist.Any()),代码看起来很凌乱。如果有 Foo.IsAny(myList),将会更加清晰。
这篇文章没有给出答案:IEnumerable is empty?

1
如果这不是评论,我可能会给你答案 :) - Schultz9999
2
在我看来,这似乎是一个 XY 问题。你应该问“如何改进我的设计,以便我不必到处检查 null?”而不是问“如何在所有地方精确检查 null 而不那么麻烦?” - sara
1
你可以使用以下代码替代:myCollection?.FirstOrDefault() == null - Adel Tabareh
23个回答

241

当然你可以这样写:

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

不过,要小心的是,并非所有序列都是可重复的;通常情况下,我更喜欢只遍历一次,以防万一。


13
这是一个好的编程模式吗?我建议去掉那里的“this”——在我的看法中,假设扩展方法会被调用时传入"null"参数是一种不好的设计风格。 - Mormegil
38
为什么?扩展方法最终使C#能够处理空值,这是其他语言(如Ruby)完全理所当然的功能。 - Matt Greer
6
为什么这必然是不好的?就像在这种情况下,有时候这非常方便,因为它让你更加统一地对待事物,并减少了特殊情况。 - Mr. Putty
6
@Mormegil 呃,我对此感到不太兴奋。只要意图清楚等方面没问题就好了。 - Marc Gravell
12
.Any()是一个扩展方法,它作用于IEnumerable<T>(或IQueryable<T>, 不过那是另一种情况)。这个方法会消耗序列,至少部分地(尽管这意味着它已被消耗) - 它可能只需要读取一个元素(特别是如果没有谓词)。因此,由于序列(IEnumerable<T>)不需要可重复性,这可能就是答案。没有谓词的Any() 基本上等价于 foreach(var x in sequence) { return true; } return false; - 尽管它使用 GetEnumerator() 等而不是编译器语法。 - Marc Gravell
显示剩余11条评论

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

9
好的,稍微有些不对,OP要求的是IEnumerable,而不是IEnumerable<T> ;-) - yoyo
15
是的,IEnumerable 没有 Any() 扩展方法。 - Blaise
1
添加 System.Linq 以启用 Any() 扩展。 - Sajithd
为什么不直接写成 'enumerable.count > 0' 呢? - haddassah S
@haddassahS 的计数必须逐个列举数值,因此速度较慢。 - Skyl3lazer
@haddassahS的计数必须逐个列举数值,所以速度较慢。 - undefined

31
这里是 @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;
    }
}

24
if (collection?.Any() == true){
    // if collection contains one or more items
}
if (collection?.Any() != true){
    // if collection is null
    // if collection does not contain any item
}

1
简单而实用。 - aca

23

另一种方法是获取枚举器并调用MoveNext()方法来查看是否有任何项:

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

这对于IEnumerable和IEnumerable<T>同样适用。


4
如果这个集合是多线程感知的,那么你需要调用Dispose来释放此枚举器吗? 是的。 - TamusJRoyce
4
请注意,您的陈述仅适用于“IEnumerable<T>”,因为非泛型的“IEnumerable”不实现“IDisposable”。 - Ian Kemp

14

我采用一些现代化的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 == 0Count() == 0来检查集合是否为空。始终使用Linq的.Any()方法。


3
Count == 0 就可以了,可能比 Any() 还要快。但是你关于 Count() == 0 不好的说法是正确的。对于那些想知道的人来说,Count() 遍历整个集合,所以如果它非常大,可能会增加很多开销! - Anthony Nichols
Count() 只有在无法转换为 ICollection 时才会迭代枚举。换句话说,当您调用此方法时,如果对象上已经有 Count 属性,则它将返回该属性,并且性能应该相同。在此处查看实现:https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,1191 - Ronald Rey
如果您正在使用IEnumerable,使用Count()来测试是否为空绝对是一个坏主意,因为Linq实现将遍历整个集合,而Any只会移动迭代器一次。请记住,在这种情况下,您不能使用Count属性,因为它不是IEnumerable接口的一部分。因此,在我看来,仅使用Any()在所有情况下测试空性总是一个更好的主意。 - Ronald Rey
1
漂亮的例子,展示了取反操作符!在某些情况下是多么难以阅读,特别是在第二个选项中;) - Fabio

9

从C#6开始,您可以使用null传播myList?.Any() == true

如果您仍然觉得这太冗长,或者更喜欢一个好的扩展方法,我建议使用Matt Greer和Marc Gravell的答案,但为了完整性而进行了一些扩展功能。

他们的答案提供了相同的基本功能,但每个人都从另一个角度来看。 Matt的答案使用string.IsNullOrEmpty的思维方式,而Marc的答案则采用Linq的.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);
    }

所以,你仍然可以像使用常规的 .Any() 一样做一些事情,例如:myList.AnyNotNull(item=>item.AnswerToLife == 42);,但增加了 null 检查。
请注意,在 C#6 的方式中:myList?.Any() 返回一个 bool? 而不是 bool,这实际上是传播 null 的效果。

1
集合的问题在于Collection?.Any()不是传递性的。当为空时,collection?.Any() == true为false,但collection?.Any() == false也为false。此外,!collection?.Any() == false也是错误的... - Jakub Szułakiewicz

8

这可能会有所帮助

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

当涉及到引用(可空)类型并且不期望在项目中出现 null 项时,可以使用此行进行验证。

myCollection?.FirstOrDefault() == null

3

Jon Skeet的答案(https://dev59.com/RF4b5IYBdhLWcg3wnSwd#28904021)使用扩展方法-Any()来处理NULL和EMPTY的情况,但是他在非NULL情况下验证了问题所有者。因此,请小心地将Jon的方法更改为对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://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any?view=netframework-4.8#moniker-applies-to


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接