【问题标题】:Does C# have IsNullOrEmpty for List/IEnumerable?C# 是否有用于 List/IEnumerable 的 IsNullOrEmpty?
【发布时间】:2012-01-24 19:46:02
【问题描述】:

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

  1. 我必须明确地检查和处理空值,避免错误和攻击。
  2. 之后很容易进行??操作得到返回值。

对于字符串,我们有 IsNullOrEmpty。 C# 本身是否有任何东西为 List 或 IEnumerable 做同样的事情?

【问题讨论】:

标签: c# ienumerable isnullorempty


【解决方案1】:

框架中没有任何内容,但它是一种非常直接的扩展方法。

See here

/// <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 的额外步骤(在可能的情况下)。我没想到会做的事情。

【讨论】:

  • 必须小心,尽管对enumerable.Any() 的调用可能会丢失不可回退枚举的元素。您可以将它包装在可以跟踪第一个元素的东西中(当然也可以很好地处理 null 或者我们否定 OP 的整个问题),但在某些情况下与空枚举合并并使用结果会更方便.
  • 是否也应该尝试转换为非泛型ICollection,因为IEnumerable&lt;Animal&gt; 可能类似于List&lt;Cat&gt; 的实例,它没有实现ICollection&lt;Animal&gt;,但实现了非泛型ICollection?
  • 在这里看到:msdn.microsoft.com/en-us/library/bb337697%28v=vs.110%29.aspx(检查备注)“一旦可以确定结果,就停止源的枚举。”这让我认为集合的强制转换是不必要的,因为 enumerable.Any() 也应该有 O(1) 。那么,如果您真的不知道它是一个列表,为什么还要转换到一个列表呢?
  • 现在可以写成一行:return a == null || ((a as ICollection)?.Count == 0) || !a.Any();
  • @ElMac ICollection.Count 的规范是跟踪计数,任何实现都应该返回计数而不需要迭代值来按需计算。但是正如另一个人在 cmets 中所说,对于 IReadOnlyCollection 也应该调用相同的优化。此外,LINQ.Any 使用 ICollection 上的优化,而不是 IReadOnlyCollection。
【解决方案2】:

后期更新:从 C# 6.0 开始,null-propagation operator 可以用来表达简洁如下:

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

或者,作为IEnumerable&lt;T&gt; 的更简洁和更通用的替代方案:

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

注意 1: 所有上层变体实际上都反映了IsNotNullOrEmpty,与 OP 问题 (quote) 不同:

由于运算符优先级,IsNullOrEmpty 等效项看起来不那么吸引人:
if (!(list?.Count &gt; 0))

注意 2:?? false 是必要的,原因如下(摘要/引用自 this post):

如果子成员是null?. 运算符将返回null。 但是 [...] 如果我们尝试获取非Nullable 成员,例如 Any() 方法,返回 bool [...] 编译器将 在Nullable&lt;&gt; 中“包装”一个返回值。例如,Object?.Any() 将 给我们bool?(即Nullable&lt;bool&gt;),而不是bool。 [...] 因为它不能被隐式转换为 bool 这个表达式不能在 if 中使用

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

在多线程上下文中,如果 [enumerable] 可以从另一个 线程(因为它是一个可访问的字段,或者因为它是 在暴露给另一个线程的 lambda 中关闭)然后 每次计算的值都可能不同 [i.e.prior null-check]

【讨论】:

  • @GrimR3 是的,你是对的:这个问题更加棘手。我这样写是为了与引用/引用内联。
  • 你可以用这个检查 0 或 1 个元素:(myArray?.Length ?? 0) &lt; 2
【解决方案3】:

没有内置任何东西。

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

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

  return !enumerable.Any();
}

【讨论】:

  • 我想知道为什么这个答案和接受的答案都错过了!在 Any() 调用之前。同样的错误被传播到我团队的源代码中。
【解决方案4】:
var nullOrEmpty = list == null || !list.Any();

【讨论】:

    【解决方案5】:

    将前面的答案组合成一个简单的 C# 6.0+ 扩展方法:

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

    【讨论】:

      【解决方案6】:

      如果您需要能够在元素不为空的情况下检索所有元素,那么这里的一些答案将不起作用,因为在不可回退的枚举上调用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&lt;MyType&gt;()).ToList()等。

      【讨论】:

      • 你能给我一个 .NET 中不可回退集合的例子吗?我的意思是框架中现有类的名称,而不是要求您给我一个示例实现。
      • @AaronLS 大多数 Linq 查询的结果。
      • 这种情况下的具体类型是什么?我知道 IQueryable 不是真正的列表,直到您尝试 ToList/foreach 时它才具体化。我想 IQueryable 有一些中间具体类型,它是引擎盖下的仅向前数据读取器,所以你说的有道理,我只是无法想象你会在哪里实际遇到这个问题。如果你foreach(var item in someQueryable) 会生成它自己的枚举器,然后someQueryable.Any() 将是一个单独的查询并且不会影响当前的 foreach 枚举器 AFAIK。
      • 不是挑战你,只是想解释我的困惑。
      • @AaronLS 它将是一个单独的枚举器,这将需要您再次运行查询,这已经够糟糕的了。如果它被转换为IEnumerable,那么更是如此,因为查询将丢失。
      【解决方案7】:

      正如其他人所说,框架中没有内置任何内容,但如果您使用的是 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;
                  }
                  ...
      

      【讨论】:

        【解决方案8】:

        我修改了 Matthew Vines 的建议以避免“IEnumerable 的可能多重枚举”问题。 (另请参阅 Jon Hanna 的评论)

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

        【讨论】:

          【解决方案9】:
          var nullOrEmpty = !( list?.Count > 0 );
          

          【讨论】:

          • 仅代码的答案不是好的答案,请尝试添加几行来解释问题所在以及您的代码如何解决问题
          • 如果 list 是 IEnumerable 则不会编译。
          【解决方案10】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-04-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-24
            • 2011-10-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多