【问题标题】:Why doesn't Any() work on a c# null object为什么 Any() 不适用于 c# null 对象
【发布时间】:2012-07-17 07:17:27
【问题描述】:

在空对象上调用 Any() 时,它会在 C# 中引发 ArgumentNullException。如果对象为空,则肯定没有“任何”,它可能应该返回 false。

为什么 C# 会这样?

【问题讨论】:

  • null 和空序列是不同的。
  • This question 提供了一些有用的方法来实现 null 检查,如果您发现在使用 .Any() 之前每次都手动执行它很笨重

标签: c# .net object ienumerable


【解决方案1】:

Any() 在问:“这个盒子里有物品吗?”

如果方框是空的,答案显然是否定的。

但是如果一开始就没有盒子,那么这个问题就没有意义了,函数会抱怨:“你到底在说什么?没有盒子。”


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

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

这可以与所有 LINQ 方法和foreach 结合使用,而不仅仅是.Any()

【讨论】:

  • 没有勺子
  • 所以我想知道为什么没有 AnyOrDefault 选项......来处理“没有勺子”的场景......
  • @Dscoduc 您可以使用?.Any() ?? false?.Any() == true。或者.OrEmpty().Any(),如果您想使用我的答案中的扩展方法。
  • @Dscoduc 我只是使用这个扩展来检查是否为空或 null public static bool IsNullOrEmpty&lt;T&gt;(this IEnumerable&lt;T&gt; list) =&gt; list == null || !list.Any();
【解决方案2】:

使用现代 C#,您可以通过如下简单检查轻松处理 OP 的场景:

List<string> foo = null;

if (foo?.Any() ?? false)
{
    DoStuff();
}

这有点像一个蹩脚的 AnyOrDefault(bool default) 实现,OP 期望 Any() 扩展方法来做。

你可以很容易地把它变成这样的扩展:

public static bool HasItems<T>(this IEnumerable<T> source)
{
    return (source?.Any() ?? false);
}

老实说,我真的不喜欢 AnyOrDefault 这个名字,因为传入默认值是没有意义的(默认值 true 可能对以后阅读代码的人来说是非常卑鄙的)。 按照 cmets 中的建议重命名为 HasItems。这是一个更好的名字!

【讨论】:

  • 这是一个很好的解决方案,但是如果您经常这样做,那么编写自己的扩展方法来精确地执行此操作会更简洁,尤其是如果您将其嵌入到三元表达式或类似的表达式中。
  • public static bool IsNullOrEmpty&lt;T&gt;(this IEnumerable&lt;T&gt; source) { return source?.Any() != true; } 类似于String.IsNullOrEmpty(stringything)
  • 小意见:我个人觉得if (foo?.Any() == true).. ?? false) 更容易掌握。英语中的前者是“if any is true then ..”,而后者本质上是一个两步逻辑语句;更难解释。它类似于“如果有(但将 null 视为 false)则 ..”。
  • AnyOrDefault 的替代名称可能是 HasItems 或更明确的 AnyAndNotNull。 FWIW,AnyOrDefault 中的“默认”指的是 Source 类型的默认值,即 IEnumerable 的“null” - 与布尔值的默认值“false”无关。所以这个名字有误导性,因为AnyOrDefault 在逻辑上意味着“任何或空(因为这是默认值)”,所以会返回true 为空。这显然不是你的意思。
  • 现在应该叫HasItems() 无论是空列表还是空名称仍然正确
【解决方案3】:

在处理引用类型时,null 值在语义上与“空”值不同。

null 字符串与 string.Empty 不同,null IEnumerable&lt;T&gt;Enumerable.Empty&lt;T&gt;(或该类型的任何其他“空”可枚举)不同。

如果Any 不是扩展方法,则在null 上调用它会导致NullReferenceException。由于它一个扩展方法,抛出一些异常(尽管不是必需的)是一个好主意,因为它保留了尝试在null 上调用方法的众所周知的语义:BOOM!

【讨论】:

  • 可能值得一提的是,为什么区分空集合和未初始化集合很重要。我唯一能想到的是它会告诉你集合是否占用了内存空间,但我几乎不是 C# 大师。
  • 虽然这是正确的,但我相信这些都是过时的语义。某人通常想要从“Any()”/“Where()”/“Select()”/等中得到什么。就此而言,调用甚至“foreach”迭代是要知道那里是否有任何东西,如果那里有什么东西就做某事。无意义的实例化或空值检查存在如此多的代码浪费。我们得到了?操作员。全力以赴,在扩展方法上为我们提供不错的 null 功能。
  • 坏坏坏主意,帮助无助的前卫...需要使用一个笨拙的(Source?.Any()).GetValueOrDefault()来简单检查strict on场景中是否存在空虚'或'不存在...不好来自 linq 的人的决定:他们应该永远记住,他们的东西是为那些知道自己在做什么的人准备的,而不是为像我这样一无所知的人准备的! ://
【解决方案4】:

Any() 是一个扩展方法,所以this 实际上是作为第一个参数传递给该方法的。在这种情况下,它抛出 ArgumentNullException is this is null 是可以理解的。

您可以事先自行检查:

bool hasAny = yourData == null ? false : yourData.Any(yourPredicate);

【讨论】:

    【解决方案5】:

    因为 Any() 是这样的扩展方法:

    public static bool Any(this IEnumerable enumerable)
    {
        if (enumerable == null)
            throw ArgumentNullException("enumerable");
        ...
    }
    

    【讨论】:

      【解决方案6】:

      Any 方法针对IEnumerable 运行,并告诉您枚举中是否有任何项目。如果你不给它任何枚举,那么 ArgumentNullException 是合理的:没有(匹配)元素的集合与没有集合不同。

      【讨论】:

        【解决方案7】:

        正如其他人已经提到的,Any 检查序列是否包含元素。它不会阻止您传递 null 值(首先可能是什么错误)。

        如果sourcenullEnumerable class 中的每个扩展方法都会抛出一个ArgumentNullException。在扩展中抛出ArgumentNullExceptions 实际上是good practise

        【讨论】:

          【解决方案8】:

          Any() 是一个扩展方法,如果源为 null,则抛出 ArgumentNullException。你会什么都不做吗?一般来说,最好获得一些关于代码中正在发生的事情的明确指示,而不是默认值。

          但这并不意味着它不能那样。如果您知道自己在做什么,请编写自己的自定义实现。

          我只是想与您分享一些我公司正在遵循的实用建议。 我们编写与私有 NuGet 共享的自定义包,这些包在我们的产品中广泛使用。检查列表是否为空/空是非常频繁的,因此我们决定编写Any 的实现,这使得我们的代码更短更简单。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-05-14
            • 1970-01-01
            • 1970-01-01
            • 2015-09-26
            • 2011-09-29
            相关资源
            最近更新 更多