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


154

我喜欢string.IsNullOrEmpty方法。我很想拥有可以为IEnumerable提供相同功能的东西。有吗 也许一些收集助手类?我要问的原因是,if如果状态为,则语句中的代码看起来很混乱(mylist != null && mylist.Any())。拥有它会更清洁Foo.IsAny(myList)

这篇文章没有给出答案:IEnumerable是否为空?


1
@msarchet:如果不是这样的话,我可能会给你答案:)
Schultz9999 2011年

在我看来,这似乎是一个XY问题。而不是问“如何在不麻烦的情况下精确地在任何地方检查null”,您应该问“如何改进设计,这样我就不必在所有地方检查null?”
sara 2016年

@nawfal,您链接到的问题没有特别包含null检查,因此我不会将其视为重复
Mygeen

Answers:


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#提供了一些使用null的功能,而其他语言(如Ruby)则完全认为这是理所当然的。
马特·格里尔

5
为什么这一定不好?在这种情况下,它有时非常方便,因为它使您可以更均匀地处理事物,并减少特殊情况。
Putty先生

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
好吧,不完全是,OP要求使用IEnumerable,而不是IEnumerable <T> ;-)
yoyo 2014年

8
是的,IEnumerable没有Any()扩展名。
布莱斯2015年

23

这是@Matt Greer的有用答案的修改版本,其中包括静态包装器类,因此您可以将其复制粘贴到新的源文件中,而不依赖Linq,并添加通用IEnumerable<T>重载以避免对值类型进行装箱非通用版本会发生这种情况。[编辑:请注意,使用of 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请注意,您的声明仅适用于true 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 == 0Count() == 0仅检查集合是否为空。始终使用Linq的.Any()


2
Count == 0很好。...也许比Any()快?但是,您对Count()== 0不好的观点是正确的。对于那些想知道Count()遍历整个集合的人,因此如果Count()很大,可能会增加大量的开销!
安东尼·尼科尔斯

Count()仅在无法将其强制转换为ICollection时才迭代该枚举。换句话说,当您调用此方法时,如果对象上已经具有Count属性,它将仅返回该属性,并且性能应相同。在此处查看实施情况:referencesource.microsoft.com/#System.Core/System/Linq/…–
罗纳德·雷

如果您使用的是IEnumerable,则使用Count()来测试是否为空绝对不是一个好主意,因为Linq实现将对整个集合进行迭代,而Any只会将迭代器移动一次。请记住,在这种情况下,您不能使用Count属性,因为它不是IEnumerable接口的一部分。我认为,这就是为什么在所有情况下仅使用Any()来测试是否为空始终是一个更好的主意的原因。
罗纳德·雷伊

否定式运算符!可能是一个很好的例子,特别是在第二种选择中;)
Fabio

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开始,您可以使用null传播myList?.Any() == true

如果您仍然觉得这个问题过于冗长或不喜欢使用一种很好的扩展方法,我会推荐Matt Greer和Marc Gravell的答案,但其中有些扩展功能可以确保完整性。

他们的答案提供了相同的基本功能,但每个答案都是从另一个角度出发的。马特(Matt)的答案使用string.IsNullOrEmpty-mentality,而马克(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);
    }

所以,你仍然可以做这样的事情: myList.AnyNotNull(item=>item.AnswerToLife == 42);你可以与常规的.Any(),但与添加的空校验

请注意,使用C#6方式:myList?.Any()返回a bool?而不是a bool,这是传播 null 的实际效果


1
collection..Any()的问题是那不是可传递的。如果为null,则collection?.Any()== true为false,但是collection?.Any()== false也是false。此外,!collection?.Any()== false也是错误的……
JakubSzułakiewicz

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

这是Marc Gravell的答案中的代码,以及使用它的示例。

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()开始逐步执​​行序列。我怀疑罗伯特·哈维(Robert Harvey)的答案意味着您通常不需要检查null 清空。通常,您可以只检查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?看起来会抛出异常,但是可以在null实例上调用扩展方法
Don Kirkby 2013年

2

我用Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false);。希望这可以帮助。

分解:

Collection?.Any()null如果Collection为null,并且Collection为空,则将返回false

Collection?.Any()??falsefalse如果Collection为空,并且falseCollection为,将给我们null

补充将给我们IsEmptyOrNull


2

乔恩·斯基特(Jon Skeet)的anwser(https://stackoverflow.com/a/28904021/8207463)使用扩展方法-Any()表示NULL和EMPTY,这是一种很好的方法。但是他正在验证问题的所有者,以防万一。因此,请谨慎地将Jon的验证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/zh-cn/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答案建立了这个

他完美地回答了OP的问题。

我想要这样的东西,同时保持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为null,这将不起作用,这是眼前的问题
Timotei '18

0

我用这个:

    public static bool IsNotEmpty(this ICollection elements)
    {
        return elements != null && elements.Count > 0;
    }

以je

List<string> Things = null;
if (Things.IsNotEmpty())
{
    //replaces ->  if (Things != null && Things.Count > 0) 
}

0

由于一次读取后会耗尽一些资源,所以我想为什么不将检查和读取结合起来,而不是传统的单独检查然后读取。

首先,我们有一个简单的检查空内联扩展名:

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

然后,我们要多花点时间(至少是我编写它的方式)来检查是否为空的内联扩展:

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,而不仅仅是与LINQ方法结合使用.Any(),这就是为什么我更喜欢它而不是人们在这里建议的更专门的帮助器函数。


0

我用

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

如果不匹配,则结果将为null,否则返回对象之一

如果您需要该列表,我相信离开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);
}
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.