【问题标题】:How to check if IEnumerable is null or empty?如何检查 IEnumerable 是 null 还是空?
【发布时间】:2011-06-30 03:51:28
【问题描述】:

我喜欢string.IsNullOrEmpty 方法。我很想拥有一些允许 IEnumerable 具有相同功能的东西。有这样的吗?也许一些收集助手类?我问的原因是在if 语句中,如果模式是(mylist != null && mylist.Any()),代码看起来很混乱。拥有Foo.IsAny(myList) 会更干净。

这篇文章没有给出答案:IEnumerable is empty?

【问题讨论】:

  • @msarchet:如果这不是评论,我可能会给你答案:)
  • 对我来说这似乎是一个 XY 问题。而不是问“我怎样才能在所有地方都检查 null 而不会那么麻烦”,而应该问“我怎样才能改进我的设计,这样我就不必到处检查 null 了?”
  • 您可以改用以下行:myCollection?.FirstOrDefault() == null

标签: c# .net linq collections ienumerable


【解决方案1】:

您确定可以这样写:

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

但是,请注意并非所有序列都是可重复的; 一般我宁愿只带他们走一次,以防万一。

【讨论】:

  • 这是一个好的模式吗?我会把this 放在那里——我认为假设在null 上调用的扩展方法是丑陋设计的标志。
  • @Mormegil 为什么?扩展方法最终使 C# 能够处理空值,而其他语言(如 Ruby)完全认为这是理所当然的。
  • 为什么这必然不好?在这种情况下,它有时非常方便,因为它可以让您更均匀地处理事物并减少特殊情况。
  • @Mormegil meh - 我对此兴奋不已。只要意图明确,等等。
  • @Miryafa .Any() 是在IEnumerable&lt;T&gt;(或IQueryable&lt;T&gt;,虽然这是不同的场景)上运行的扩展方法。这样做消耗序列,至少部分(尽管这仍然意味着它被消耗) - 它可能只需要读取一个元素(尤其是在没有谓词的情况下)。因此,由于序列 (IEnumerable&lt;T&gt;) 不需要是可重复的,可能就是这样。没有谓词的Any()本质上等同于foreach(var x in sequence) { return true; } return false;——尽管它使用GetEnumerator()等而不是编译器语法
【解决方案2】:
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) {
    return enumerable == null || !enumerable.Any();
}

【讨论】:

  • 好吧,不完全是,OP 要求的是 IEnumerable,而不是 IEnumerable ;-)
  • 是的,IEnumerable 没有 Any() 扩展名。
【解决方案3】:

这是 @Matt Greer 的有用答案的修改版本,其中包括一个静态包装类,因此您可以将其复制粘贴到一个新的源文件中,不依赖于 Linq,并添加一个通用的 IEnumerable&lt;T&gt; 重载,以避免在非泛型版本中出现的值类型的装箱。 [编辑:请注意,IEnumerable&lt;T&gt; 的使用不会阻止枚举数的装箱,duck-typing 不能阻止这种情况,但至少值类型集合中的元素不会被装箱。]

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

【讨论】:

    【解决方案4】:

    另一种方法是获取 Enumerator 并调用 MoveNext() 方法查看是否有任何项目:

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

    这适用于 IEnumerable 和 IEnumerable

    【讨论】:

    • 你应该在这个枚举器上调用 dispose 吗?如果集合是多线程感知的?是的。 stackoverflow.com/questions/13459447/…
    • @TamusJRoyce 请注意,您的陈述仅适用于 IEnumerable&lt;T&gt;,因为非泛型 IEnumerable 不实现 IDisposable
    【解决方案5】:

    我这样做的方式,利用了一些现代 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()

    【讨论】:

    • Count == 0 就好了....可能比 Any() 更快?但是,您对 Count() == 0 不好是正确的。对于那些想知道 Count() 遍历整个集合的人,所以如果它很大可能会增加大量开销!
    • Count() 仅在无法转换为 ICollection 时迭代枚举。换句话说,当你调用这个方法时,如果对象上已经有一个 Count 属性,它只会返回它,并且性能应该是相同的。在此处查看实现:referencesource.microsoft.com/#System.Core/System/Linq/…
    • 如果您正在使用 IEnumerable,使用 Count() 来测试是否为空绝对是个坏主意,因为 Linq 实现将遍历整个集合,而 Any 只会移动迭代器一次。请记住,在这种情况下您不能使用 Count 属性,因为它不是 IEnumerable 接口的一部分。这就是为什么在我看来,在所有场景中使用 Any() 来测试是否为空总是一个更好的主意。
    • 不可读的否定运算符 ! 的好例子,特别是在第二个选项中;)
    【解决方案6】:
    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
    }
    

    【讨论】:

      【解决方案7】:

      这可能会有所帮助

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

      【讨论】:

        【解决方案8】:

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

        如果您仍然觉得这太麻烦或者更喜欢一个好的 ol' 扩展方法,我会推荐 Matt Greer 和 Marc Gravell 的答案,但为了完整性,还需要一些扩展功能。

        他们的答案提供了相同的基本功能,但每个都从另一个角度。 Matt 的回答使用了string.IsNullOrEmpty-mentality,而 Marc 的回答采用了 Linq 的 .Any() 方法来完成工作。

        我个人倾向于使用.Any() 路,但想从方法的other overload 中添加条件检查功能:

            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=&gt;item.AnswerToLife == 42); 与常规 .Any() 一样,但添加了空检查

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

        【讨论】:

        • 集合的问题?.Any() 是不可传递的。当为空时,collection?.Any() == true 为 false,但 collection?.Any() == false 也为 false。此外, !collection?.Any() == false 也是错误的......
        【解决方案9】:

        这是来自Marc Gravell's answer 的代码,以及使用它的示例。

        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's answer 的意思是您通常不需要检查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) 实际上会开始逐步执​​行该序列。 (当然,如果您使用大量序列,您很快就会学会思考通过它们的步骤。)

        【讨论】:

        【解决方案10】:

        我使用Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false);。希望这会有所帮助。

        细分:

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

        如果 Collection 为空,Collection?.Any()??false 将给我们false,如果 Collection 是null,则给我们false

        对此的补充将给我们IsEmptyOrNull

        【讨论】:

          【解决方案11】:

          Jon Skeet 的 anwser (https://stackoverflow.com/a/28904021/8207463) 有一个使用扩展方法的好方法 - Any() 用于 NULL 和 EMPTY。但他正在验证问题的所有者,以防不为空。 因此,请小心地将 Jon 验证 AS 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

          【讨论】:

            【解决方案12】:

            我遇到了同样的问题,我解决了它:

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

            "c=>c!=null" 将忽略所有空实体。

            【讨论】:

              【解决方案13】:

              我在answer by @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;
                  }
              }
              

              扩展方法的命名可能会更好。

              【讨论】:

                【解决方案14】:

                当涉及引用(可为空)类型时,可以使用这一行进行验证

                myCollection?.FirstOrDefault() == null
                

                【讨论】:

                  【解决方案15】:

                  我使用了简单的 if 来检查它

                  查看我的解决方案

                  foreach (Pet pet in v.Pets)
                  {
                      if (pet == null)
                      {
                          Console.WriteLine(" No pet");// enumerator is empty
                          break;
                      }
                      Console.WriteLine("  {0}", pet.Name);
                  }
                  

                  【讨论】:

                    【解决方案16】:

                    以下检查是否为空的其他最佳解决方案?

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

                    【讨论】:

                    • 如果listEnumerable 为空,这将不起作用,这是手头的问题
                    【解决方案17】:

                    我用这个:

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

                    【讨论】:

                      【解决方案18】:

                      既然有的资源在一次读取后就耗尽了,我想为什么不将检查和读取结合起来,而不是传统的单独检查,然后读取。

                      首先,我们有一个更简单的 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 inline extension:

                      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,以便调用者可以包含错误的备用名称,如果它不是被检查的“源”,例如“名称(目标)”。

                      【讨论】:

                        【解决方案19】:
                         public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source)
                            {
                                return source != null && source.Any();
                            }
                        

                        我自己的扩展方法来检查 Not null 和 Any

                        【讨论】:

                          【解决方案20】:

                          如果没有自定义助手,我推荐?.Any() ?? false?.Any() == true,它们比较简洁,只需要指定一次序列。


                          当我想将丢失的集合视为空集合时,我使用以下扩展方法:

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

                          此函数可以与所有 LINQ 方法和 foreach 结合使用,而不仅仅是 .Any(),这就是为什么我更喜欢它而不是人们在这里提出的更专业的帮助函数。

                          【讨论】:

                            【解决方案21】:

                            我用

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

                            如果不匹配则结果为空,否则返回其中一个对象

                            如果您想要列表,我相信离开 First() 或调用 ToList() 将提供列表或 null。

                            【讨论】:

                              【解决方案22】:

                              null 将返回 true

                              enter    public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
                                  {
                              
                                      try
                                      {
                                          return enumerable?.Any() != true;
                                      }
                                      catch (Exception)
                                      {
                              
                                          return true;
                                      }
                                 
                                  }
                              

                              代码在这里

                              【讨论】:

                                【解决方案23】:

                                只需添加using System.Linq 并在您尝试访问IEnumerable 中的可用方法时看到神奇的发生。添加它将使您可以访问名为Count() 的方法,就这么简单。请记住在调用count() 之前检查null value :)

                                【讨论】:

                                  【解决方案24】:

                                  看看这个开源库:Nzr.ToolBox

                                  public static bool IsEmpty(this System.Collections.IEnumerable enumerable)
                                  

                                  【讨论】:

                                  • 那是您要推广的图书馆,对吗?今后请注意。不过,看起来像一个可靠的集合,感谢您将其公开。
                                  猜你喜欢
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2021-04-19
                                  • 2012-05-07
                                  • 2017-04-21
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2018-05-09
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多