简体   繁体   English

为什么 Any() 在 c# null object 上不起作用

[英]Why doesn't Any() work on a c# null object

When calling Any() on a null object, it throws an ArgumentNullException in C#. If the object is null, there definitely aren't 'any', and it should probably return false.在 null object 上调用Any()时,它会在 C# 中抛出 ArgumentNullException。如果 object 是 null,则肯定没有“任何”,它可能应该返回 false。

Why does C# behave this way?为什么 C# 会这样?

Any() is asking: "Does this box contain any items?" Any()在问:“这个盒子里有任何物品吗?”

If the box is empty, the answer is clearly no.如果盒子是空的,答案显然是否定的。

But if there is no box in the first place, then the question makes no sense, and the function complains: "What the hell are you talking about? There is no box."但是如果一开始就没有盒子,那么这个问题就没有意义了,函数就会抱怨:“你到底在说什么?没有盒子。”


When I want to treat a missing collection like an empty one, I use the following extension method:当我想将丢失的集合视为空集合时,我使用以下扩展方法:

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

This can be combined with all LINQ methods and foreach , not just .Any() .这可以与所有 LINQ 方法和foreach结合,而不仅仅是.Any()

With modern C#, you can easily handle the OP's scenario with a simple check like this:使用现代 C#,您可以通过如下简单检查轻松处理 OP 场景:

List<string> foo = null;

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

This is kinda like a lame AnyOrDefault(bool default) implementation that the OP is expecting the Any() extension method to do.这有点像 OP 期望Any()扩展方法执行的蹩脚的AnyOrDefault(bool default)实现。

You could easily make this into an extension like this:你可以很容易地把它变成这样的扩展:

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

Honestly, I don't really like the name AnyOrDefault for this since it won't ever make sense to pass in a default value (a default of true would probably be pretty mean to people reading code later).老实说,我真的不喜欢AnyOrDefault这个名字,因为传递默认值是没有意义的(默认值 true 可能对以后阅读代码的人来说是非常卑鄙的)。 Renamed to HasItems , as suggested in the comments.根据评论中的建议,重命名为HasItems This is a far better name!这是一个更好的名字!

When dealing with reference types, a null value is semantically different from an "empty" value.在处理引用类型时, null值在语义上与“空”值不同。

A null string is not the same as string.Empty , and a null IEnumerable<T> is not the same as Enumerable.Empty<T> (or any other "empty" enumerable of that type). null字符串与string.Empty不同,并且null IEnumerable<T>Enumerable.Empty<T> (或该类型的任何其他“空”可枚举)不同。

If Any were not an extension method, calling it on null would result in NullReferenceException .如果Any不是扩展方法,则在null上调用它会导致NullReferenceException Since it is an extension method, throwing some exception (although not necessary) is a good idea because it preserves the well-known semantics of trying to call a method on null : BOOM!由于它一种扩展方法,因此抛出一些异常(尽管不是必需的)是一个好主意,因为它保留了尝试在null上调用方法的众所周知的语义: BOOM!

Any() is an extension method, so this is actually passed as the first argument to the method. Any()是一个扩展方法,所以this实际上是作为该方法的第一个参数传递的。 In this situation, it's understandable for it to throw ArgumentNullException is this is null .在这种情况下,它抛出ArgumentNullException is this is null是可以理解的。

You can perform the check yourself beforehand:您可以事先自行执行检查:

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

Because Any() it is a extension method like this:因为 Any() 它是这样的扩展方法:

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

The Any method runs against an IEnumerable and tells you whether there are any items in the Enumerable . Any方法针对IEnumerable运行,并告诉您Enumerable 中是否有任何项目。 If you don't give it anything to enumerate then an ArgumentNullException is reasonable: a collection with no (matching) elements is different to no collecion.如果您不给它任何枚举,那么 ArgumentNullException 是合理的:没有(匹配)元素的集合与没有集合不同。

As others have already mentioned, Any checks whether or not a sequence contains elements.正如其他人已经提到的, Any检查序列是否包含元素。 It does not prevent you from passing null values(what might the bug in the first place).它不会阻止您传递null值(首先可能是什么错误)。

Every extension method in Enumerable class throws an an ArgumentNullException if the source is null .如果sourcenull ,则Enumerable中的每个扩展方法都会引发ArgumentNullException Throwing ArgumentNullExceptions in extensions actually is good practise .在扩展中抛出ArgumentNullExceptions实际上是一种很好的做法

Any() is an extension method that throws ArgumentNullException if the source is null. Any()是一种扩展方法,如果源为 null,则抛出ArgumentNullException Would you perform an action on nothing?你会什么都不做吗? In general, it's better to get some explicit indicator of what's happening in the code rather than the default value.通常,最好获得一些关于代码中正在发生的事情的明确指示,而不是默认值。

But it doesn't mean it can't be like that.但这并不意味着它不能那样。 If you know what you doing, write your own custom implementation.如果您知道自己在做什么,请编写自己的自定义实现。

I just wanted to share with you some practical advice my company is following.我只是想与您分享一些我公司正在遵循的实用建议。 We write our custom packages shared with private NuGet that are widely used in our products.我们编写与私有 NuGet 共享的自定义包,这些包在我们的产品中广泛使用。 Checking if the list is null/empty is very frequent, so we decided to write our implementation of Any which makes our code shorter and simpler.检查列表是否为空/空是非常频繁的,因此我们决定编写Any的实现,这使得我们的代码更短更简单。

Ultimately, the distinction is due to the behavioral differences between no elements and null.最终,区别是由于无元素和空元素之间的行为差​​异。

Any() internally attempts to access the underlying sequence ( IEnumerable ). Any()在内部尝试访问底层序列( IEnumerable )。 Since the object can be null , there is no way to access the enumerable to validate it, hence a NullReferenceException is thrown to indicate this behavior.由于对象可以为null ,因此无法访问可枚举来验证它,因此会抛出NullReferenceException来指示此行为。

At first, it sounds like it should just return false since null in a way could mean none.起初,听起来它应该只返回 false,因为 null 在某种程度上可能意味着没有。

However, there is different levels of information and behavior occurring here.但是,这里发生了不同级别的信息和行为。

  • Any() with 0 elements is a collection with 0 elements.具有0个元素的Any()是具有0个元素的集合。
  • null means no collection. null表示不收集。

Any() with 0 elements is a collection.具有0个元素的Any()一个集合。 The latter isn't a collection.后者不是一个集合。


Practical real world example实际的现实世界示例

If you have a wallet (underlying sequence), with no cash (elements) inside, you have a wallet, but your broke (no elements).如果你有一个钱包(底层序列),里面没有现金(元素),你有一个钱包,但是你破产了(没有元素)。 Null means you don't even have a wallet to put cash inside. Null 意味着您甚至没有钱包可以放入现金。

There is a method in the.Net Framework for this, why they hid it here, who knows... .Net Framework 中有一个方法,为什么他们把它藏在这里,谁知道......

namespace Microsoft.IdentityModel.Tokens
{
    /// <summary>
    /// A class which contains useful methods for processing collections.
    /// </summary>
    public static class CollectionUtilities
    {
        /// <summary>
        /// Checks whether <paramref name="enumerable"/> is null or empty.
        /// </summary>
        /// <typeparam name="T">The type of the <paramref name="enumerable"/>.</typeparam>
        /// <param name="enumerable">The <see cref="IEnumerable{T}"/> to be checked.</param>
        /// <returns>True if <paramref name="enumerable"/> is null or empty, false otherwise.</returns>
        public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
        {
            return enumerable == null || !enumerable.Any();
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM