简体   繁体   English

C# 使用,=null, Count > 0. and .Any() 时的最佳实践

[英]C# Best Practices when using !=null, Count > 0, and .Any()

Out of curiosity, what are the underlying differences between !=null , Count > 0 , and .Any() , and when is the optimal time to use each?出于好奇, !=nullCount > 0.Any()之间的根本区别是什么,什么时候是使用它们的最佳时间? - for both architectural and performance. - 对于建筑和性能。

I know that .Any() is for IEnumerable s, not lists, but I find myself using them ( !=null and Count > 0 ) interchangeably when allowed to.我知道.Any()适用于IEnumerable s,而不是列表,但我发现自己在允许的情况下可以互换使用它们( !=null and Count > 0 )。 I don't want to get into the habit if it is bad practice.如果这是不好的做法,我不想养成习惯。

The way Enumerable.Any works, is the following Enumerable.Any的工作方式如下

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
        throw Error.ArgumentNull(nameof (source));
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        // Returns very early if there are any items
        if (enumerator.MoveNext())
            return true;
    }
    return false;
}

There is some overhead, but not that much, really, hence I'd assume that Any() performs comparably performant to List<T>.Count (the IEnumerable extension tries to fall back to that value, hence this should be comparable, too).有一些开销,但实际上并没有那么多,因此我假设Any()的性能与List<T>.CountIEnumerable扩展试图回退到该值,因此这也应该具有可比性).

Clarity-wise, it eventually boils down to a matter of taste.就清晰度而言,它最终归结为品味问题。 Personally I like the expressiveness of Any() , for it's a bit closer to natural language to me.我个人喜欢Any()的表现力,因为它对我来说更接近自然语言。

rows.Any()

makes me think less than让我想不到

rows.Count == 0

But not that much.但不是那么多。

null comparison works in a totally different way. null比较以完全不同的方式工作。 If this is not clear to you, you should educate yourself about value and reference types in C# ( warning : Things have become a bit different lately, since value types do not allow null by default in current versions of C#/.NET).如果您不清楚这一点,您应该在 C# 中了解值和引用类型(警告:最近情况有所不同,因为在当前版本的 C#/.NET 中默认值类型不允许使用null )。

Summarized: Reference types are stored differently and the actual variable is (kind of) just a pointer to where the data is stored (not 100% accurate).总结:引用类型的存储方式不同,实际变量(某种程度上)只是指向数据存储位置的指针(不是 100% 准确)。 Classically pointers in C++ could take the value 0 to indicate that there is no data they point to, which was carried over (at least as a concept) to C# (and other C-like languages as Java). C++ 中的经典指针可以采用值0来指示没有它们指向的数据,这被转移(至少作为一个概念)到 C#(以及其他类似 C 的语言,如 Java)。 Anyway, since the pointers are not accessed directly in C#, null was invented to indicate that the variable does not point to actual data ("there is no data associated with that variable").无论如何,由于在 C# 中不直接访问指针,因此发明了null以指示该变量不指向实际数据(“没有与该变量关联的数据”)。 Hence comparing a List<int> variable with null means that you ask whether the list instance has been created at all, which is a nescessary, but not a sufficient criteria for the actual list items being stored.因此,将List<int>变量与null进行比较意味着您询问列表实例是否已经创建,这是一个必要条件,但对于存储的实际列表项来说并不是一个充分的条件。

It depends on your goals.这取决于你的目标。 A List can be not null, but have Count == 0 . List可以不是 null,但Count == 0 What about Any() - use it in LINQ style like: Any()怎么样 - 在 LINQ 风格中使用它,例如:

if(MyEnumerable.Any(x => x.Id == myId))
{
    // do something
}
  !=null

Checks if the list is null. This may or may not represent the same thing as "no items".检查列表是否为 null。这可能代表也可能不代表“无项目”。 I would recommend to avoid using null for lists at all since the meaning is unclear.我建议完全避免将 null 用于列表,因为含义不明确。 Use an empty collection to represent "no items".使用空集合来表示“没有项目”。 If you need something else to represent "does not exist", use a maybe/optional type, or a possibly nullable reference if they are non-nullable per default.如果您需要其他东西来表示“不存在”,请使用可能/可选类型,或者如果默认情况下它们不可为空,则使用可能为空的引用。 That should be a clearer signal to the reader that "no items" and "does not exist" are different states that may need to be handled differently.这应该向读者发出更明确的信号,即“没有项目”和“不存在”是可能需要以不同方式处理的不同状态。

.Any()

This is part of LINQ, so will work for any enumerable collection.这是 LINQ 的一部分,因此适用于任何可枚举的集合。 It is very clear and convenient, but will be slightly slower than checking a property.非常清晰方便,但会比查属性稍慢。 But it allows inline filtering if you want to check the presence of some specific value.但是如果你想检查某个特定值的存在,它允许内联过滤。

.Count > 0 or .Length > 0

This will only work for collections with a Count/Length property, but should be fastest since it just does a compare.这仅适用于具有计数/长度属性的 collections,但应该是最快的,因为它只是进行比较。 At least assuming the properties are simple and does not do a bunch of work.至少假设属性很简单并且不会做很多工作。

But note that performance concerns are not very relevant unless you are running the code very frequently.但请注意,除非您非常频繁地运行代码,否则性能问题并不是很重要。 So start by measuring the performance before trying to do any optimizations.因此,在尝试进行任何优化之前,先测量性能。

TL;DR: Any() will in fact call ICollection.Count , if the collection type implements it. TL;DR: Any()实际上会调用ICollection.Count ,如果集合类型实现的话。 Otherwise it will try and pick the "best" way of checking if the collection is non-empty.否则它将尝试选择“最佳”方法来检查集合是否为非空。

Checking for null is quite different to checking for empty, and it doesn't really make sense to compare it with empty checks.检查null与检查空值有很大不同,将它与空值检查进行比较并没有什么意义。


Paul Kertscher has already answered the meat of your question, but to help with future queries on this sort of thing it is well worth remembering that the source code for do.net is, in fact, available online. Paul Kertscher 已经回答了您的问题的实质,但是为了帮助将来查询此类问题,值得记住的是,do.net 的源代码实际上可以在线获得。

The code for the LINQ Any() method, for example, may be found here: https://github.com/do.net/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/AnyAll.cs (and there's plenty more in the rest of the runtime repo, with the libraries here ).例如,LINQ Any()方法的代码可以在这里找到: https://github.com/do.net/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/ AnyAll.cs (运行时存储库的 rest 中还有更多内容,库在这里)。 It is a little more complex than the version of the method that Paul included in his answer... possibly he's referencing a different version of the runtime.它比 Paul 在他的回答中包含的方法版本稍微复杂一些……他可能引用了不同版本的运行时。 Here's the latest implementation though (comments present in original source):不过,这是最新的实现(原始来源中的评论):

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
    }

    if (source is ICollection<TSource> collectionoft)
    {
        return collectionoft.Count != 0;
    }
    else if (source is IIListProvider<TSource> listProv)
    {
        // Note that this check differs from the corresponding check in
        // Count (whereas otherwise this method parallels it).  If the count
        // can't be retrieved cheaply, that likely means we'd need to iterate
        // through the entire sequence in order to get the count, and in that
        // case, we'll generally be better off falling through to the logic
        // below that only enumerates at most a single element.
        int count = listProv.GetCount(onlyIfCheap: true);
        if (count >= 0)
        {
            return count != 0;
        }
    }
    else if (source is ICollection collection)
    {
        return collection.Count != 0;
    }

    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        return e.MoveNext();
    }
}

The code for ICollection is part of System.Collection , and IListProvider is part of System.Linq . ICollection的代码是System.Collection的一部分,而IListProviderSystem.Linq的一部分。

You can see that there's multiple ways in which Any() can test if the underlying collection has any items in it, and you can reasonably assume that they're tried in order of speed/efficiency.您可以看到Any()可以通过多种方式来测试底层集合中是否包含任何项目,并且您可以合理地假设它们是按照速度/效率的顺序进行尝试的。 Obviously, you could write a terribly implementation of ICollection.Count { get } , but that would be a silly thing to do and it would be nice to think that Microsoft haven't done that in any of the collection types that ship with the do.net runtime.显然,您可以编写一个非常棒的ICollection.Count { get }实现,但这将是一件愚蠢的事情,并且很高兴认为微软没有在 do 附带的任何集合类型中这样做.net 运行时。 (incidentally, arrays implement ICollection.Count via an explicit interface, which maps it to their Length property, which is why Any() doesn't need to care about checking for Length ) (顺便说一句, arrays通过显式接口实现ICollection.Count ,将其映射到它们的Length属性,这就是Any()不需要关心检查Length的原因)

I haven't looked up any implementations of IListProvider<T>.GetCount ,.我还没有查找IListProvider<T>.GetCount的任何实现。 The docs for the interface method say this:接口方法的文档是这样说的:

If true then the count should only be calculated if doing so is quick (sure or likely to be constant time), otherwise -1 should be returned.如果为真,则只有在快速计算(确定或可能是恒定时间)时才应计算计数,否则应返回 -1。

Again, terrible implementations could exist, but probably don't.同样,糟糕的实现可能存在,但可能不存在。

There's generally nothing wrong with using Any() instead of Count .使用Any()而不是Count通常没有错。 The additional overhead in calling a method and running through a few ifs and casts is generally negligible.调用方法和运行一些 ifs 和强制转换的额外开销通常可以忽略不计。 Just make sure not to confise Count with Count() , as the latter may require traversing the whole of a collection which can be a comparatively expensive and time consuming thing to do, and is quite pointless if all you you want to do is check for emptiness.只需确保不要将CountCount()混淆,因为后者可能需要遍历整个集合,这可能是一件相对昂贵且耗时的事情,如果您只想检查空虚。

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

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