【问题标题】:In C#, what is the cleanest way to do an Any() that includes the null case?在 C# 中,执行包含 null 大小写的 Any() 的最简洁方法是什么?
【发布时间】:2014-06-25 21:09:54
【问题描述】:

我在 C# 中有以下代码:

 if (this.Trucks.Any() || this.Cars.Any())
 {
      return true;
 }
 return false;

但我刚刚意识到有时这些数组中的一个或其中一个为 null,所以在我的情况下,我希望 null 只返回 false(而不是抛出异常)。

我可以像这样添加预先的空检查:

 if ((this.Trucks != null && this.Trucks.Any()) || 
    (this.Cars != null && this.Cars.Any()))
 {
    return true;
 }
  return false;
 }

但想看看是否有更清洁的方法

【问题讨论】:

标签: c# linq


【解决方案1】:

我建议您尽可能地使用非空合约(空对象模式)。这意味着您可能会在构造函数空数组中初始化(Enumerable.Empty<T>() 在 IEnumerable 的情况下 - 在数组的情况下添加 .ToArray() )。空值检查的问题在于,它通常会在代码上传播和成倍增加——代码(和单元测试)的复杂性会不必要地增加。忘记在x 位置检查 null 比忘记在一个位置初始化空对象更容易。您还可以使用Code contracts 库在运行时检查您的“合同”。事实上,“null”的发明者称这是十亿美元的错误:"Null References: The Billion Dollar Mistake"

【讨论】:

    【解决方案2】:

    如果您想将空序列视为空序列,我会创建一个扩展方法:

    public static class Seq
    {
        public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> seq)
        {
            return seq ?? Enumerable.Empty<T>();
        }
    }
    
    return this.Trucks.EmptyIfNull().Any() || this.Cars.EmptyIfNull().Any()
    

    如果您不喜欢使用扩展方法处理空值,您可以删除 this 修饰符并使用

    return Seq.EmptyIfNull(this.Trucks).Any() || Seq.EmptyIfNull(this.Cars).Any()
    

    【讨论】:

    • null IEnumerable 的概念有些麻烦,正如您所见,很难处理。我总是尽可能地尝试返回Enumerable.Empty&lt;T&gt; 而不是null,这是一种干净的强制方式。这个小小的扩展方法应该是每个开发者的技巧包的一部分。
    • 扩展方法的外观和行为应该像实例方法,因为将来可能会添加兼容的实例方法。因此,任何接收空引用的扩展方法都应该抛出一个NullReferenceException。
    • @PauloMorgado - 以后无法添加兼容的实例方法 - 关键是要处理 null,因此抛出 NRE 会失败。
    • @PauloMorgado - 没有人可以添加将空序列转换为空序列的实例方法。
    • @PauloMorgado - 你刚才说“我不能向IEnumerable&lt;T&gt; 添加实例方法,但微软可以”。 Microsoft 和其他任何人都无法向 IEnumerable&lt;T&gt; 添加任何内容来执行此扩展方法的功能。您的整个论点是这种方法可能会导致问题,因为“将来可能会添加兼容的实例方法”,但是不能添加这样的实例方法。
    【解决方案3】:

    制作你自己的实现:

    public static Class StaticHelpers
    {
        public static bool AnyEx<T>(this IEnumerable<T> enumerable)
        {
            if (enumerable == null) return false;
    
            return enumerable.Any();
        }
    }
    

    像这样使用它:

    if (this.Trucks.AnyEx() || this.Cars.AnyEx())
    {
        return true;
    }
    return false;
    

    【讨论】:

    • @AK_ 你可以说拥有null 集合是个坏主意(在这件事上已经有一些好的cmets),但不要称之为错误。我认为它不是。如果您不能假设集合是 null,这是解决该问题的一种方法。
    • 我不是说它是一个错误,我是说你把一些不好的东西弄得更糟了。
    • 扩展方法的外观和行为应该像实例方法,因为将来可能会添加兼容的实例方法。因此,任何接收空引用的扩展方法都应该抛出一个NullReferenceException。
    • @PauloMorgado 反驳:string.IsNullOrEmpty。总的来说,我同意。
    • string.IsNullOrEmpty。和 string.IsNullOrWhitespace 不是扩展方法,在这种情况下也是正确的方法。
    【解决方案4】:

    您只需在主要的if 条件下检查它们怎么样:

    if ((this.Trucks != null && this.Trucks.Any()) || 
        (this.Cars != null && this.Cars.Any()))
    {
      return true;
    }
    return false;
    

    这可以重写为一长行:

    return ((this.Trucks != null && this.Trucks.Any()) || 
            (this.Cars != null && this.Cars.Any()));
    

    【讨论】:

    • 这就是我上面的内容。 .我只是想看看有没有更清洁的东西。 .看起来扩展方法可能是要走的路
    • 扩展方法是一种权衡。更简洁、“更干净”的代码在前面,但隐藏了更多你正在做的事情的逻辑。
    • 我同意。我认为这是“最干净”的方法,除了简单地保证集合从一开始就永远不会为空(例如 readonly 在 ctor 中初始化的列表)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    • 2010-11-26
    • 1970-01-01
    • 2015-04-26
    • 1970-01-01
    相关资源
    最近更新 更多