【问题标题】:Return zero for Count() on null IEnumerables在 null IEnumerables 上为 Count() 返回零
【发布时间】:2021-02-10 21:10:22
【问题描述】:

我已经厌倦了使用这样的代码:

var count = 0;
if (myEnumerable != null)
{
    count = myEnumerable.Count();
}

这有点迂腐:

var count = (myEnumerable ?? new string[0]).Count();

有没有更整洁的方法来做到这一点?我曾经在 IEnumerable 上有一个(名字不好的)PhantomCount 扩展方法,它使用了我的第一个代码示例,但它有一些味道(除了名字)。

【问题讨论】:

    标签: c# .net collections


    【解决方案1】:

    问题实际上在于创建这些可枚举项。除非你有一个非常好的理由,否则任何生成可迭代集合的东西都应该返回一个空集合而不是null。这将与Null-Object-Pattern 一致,因此好处是相同的。

    我的建议是修复任何产生 myEnumerable 的问题,或者如果你不能这样做,请在前面添加一个检查方式,看看它是否为空并做出适当的反应。

    【讨论】:

    • 还有一个 +1。在设计 API 以返回空集合而不是 null 时,这是一种非常好的做法,以避免给开发人员带来负担,一直进行这样的测试。
    • 视情况而定。爱尔兰制造的所有真正优质葡萄酒的清单(这是一个空列表)和纳尼亚制造的所有真正优质葡萄酒(这是无效的,因为纳尼亚不存在)之间存在差异。有时需要区分空和空。我确实同意一个人应该倾向于返回的空。
    • @Jon:使用 null 来表示特殊情况就像汽车踩下手刹来表示油量不足。
    • @Anon 使用 null 来表示 null 条件就像,嗯,一些非常明显、直接和明智的事情。
    • @丹。我不同意,我不认为 null 是无效状态的良好指标。当“enumerable 不存在”和“enumerable 存在并且为空”都有效并且可以有目的地区分时,这是一个很好的指标。当它们都有效但没有实际区别时,空对象模式中的空可枚举(我的最爱)会摇摆不定,当一个无效时,我会抛出异常。所有这三种情况都可以在经过深思熟虑的合理代码中发生。
    【解决方案2】:

    怎么样

    count = myEnumerable == null? 0 : myEnumerable.Count()
    

    【讨论】:

    • 我有一种感觉,他会要求比这更整洁的,+1 :)
    【解决方案3】:

    我不认为使用扩展方法是一个坏主意。

    public static int NullableCount<T>(this IEnumerable<T> collection)
    {
       return collection == null ? 0 : collection.Count();
    }
    

    【讨论】:

    • +1 我做了一模一样的东西——只是把它命名为CountOrZero。个人觉得比较清楚。
    【解决方案4】:

    我使用自定义扩展方法:

    public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source)
    {
        return source ?? Enumerable.Empty<T>();
    }
    
    ...
    
    int count = myEnumerable.EmptyIfNull().Count();
    

    【讨论】:

    • +1,我认为这是列出的扩展方法解决方案中最干净的。非常明确的是,您假设 null 可枚举为空,之后您可以照常使用该可枚举。
    【解决方案5】:

    在 2019 年,最简洁的方式是var count = myEnumerable?.Count() ?? 0;

    2021 年编辑:

    public int CountNullable<T>(this IEnumerable<T>? enumerable) =>
        enumerable?.Count() ?? 0;
    

    【讨论】:

    【解决方案6】:

    我也会编写自己的扩展方法CountOrZeroForNull,如其他答案所示。

    除了...而不是:

    var count = (myEnumerable ?? new string[0]).Count();
                              // ^^^^^^^^^^^^^
    

    你可以写:

    var count = (myEnumerable ?? Enumerable.Empty<string>()).Count();
                              // ^^^^^^^^^^^^^^^^^^^^^^^^^^
    

    这并不能缓解您的具体问题,但可以避免分配未使用的数组。 (Enumerable.Empty&lt;T&gt; 很可能实现为简单的 yield break 语句。)

    【讨论】:

    • Enumerable.Empty&lt;T&gt;的当前实现实际上返回了一个单例空T[]数组。
    • @LukeH:我不会这么想的。感谢您查找此内容。
    【解决方案7】:

    只需创建您自己的扩展方法来处理您希望的空枚举。

    public int CountOrNull<T>(this IEnumerable<T> source)
    {
        return source == null ? 0 : source.Count();
    }
    

    然后您可以简单地使用:

    var list1 = new int[] { 1, 2, 3, 4 };
    var list2 = (int[])null;
    
    var count1 = list1.CountOrNull(); // 4
    var count2 = list2.CountOrNull(); // 0
    

    这就是扩展方法的伟大之处。即使(您似乎正在)调用该方法的对象是null,它们仍然可以正常工作。

    【讨论】:

    • @Noldorin: 修正了代码示例中的错字(// 4 注释);也许验证这确实是你想要的。
    • @stakx:谢谢;这确实是一个错字。
    【解决方案8】:

    如果返回值为0,你会采取什么行动?

    如果这很有趣,也许你应该像这样使用 Haack 的 IsNullOrEmpty 扩展方法 IEnumerable

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

    链接是http://haacked.com/archive/2010/06/10/checking-for-empty-enumerations.aspx

    作为评论发布在博客上,您还可以找到我写的 Exception 类:

    public class ArgumentNullOrEmptyException : ArgumentNullException
    {
        public ArgumentNullOrEmptyException( string paramName ) : base( paramName )
        {}
    
        public ArgumentNullOrEmptyException( string paramName, string message ) : base( paramName, message )
        {}
    
        public override string Message
        {
            get
            {
                return "Value cannot be null nor empty.{0}Parameter name: {1}".FormatWith( Environment.NewLine, ParamName );
            }
        }
    }
    

    【讨论】:

      【解决方案9】:
      var count = 0; 
      
      if (myEnumerable != null) 
          count = myEnumerable.Count(); 
      

      虽然它不像其他答案那样技术性强,但它是最易读的。

      【讨论】:

      • 我不认为这三行比一个有名的扩展方法更具可读性。
      • 是的,我想避免多次重复相同的检查。毕竟这是我的问题的重点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-04
      • 2013-08-06
      • 2017-11-11
      • 2011-02-17
      • 2018-05-25
      • 1970-01-01
      相关资源
      最近更新 更多