繁体   English   中英

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

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

出于好奇, !=nullCount > 0.Any()之间的根本区别是什么,什么时候是使用它们的最佳时间? - 对于建筑和性能。

我知道.Any()适用于IEnumerable s,而不是列表,但我发现自己在允许的情况下可以互换使用它们( !=null and Count > 0 )。 如果这是不好的做法,我不想养成习惯。

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;
}

有一些开销,但实际上并没有那么多,因此我假设Any()的性能与List<T>.CountIEnumerable扩展试图回退到该值,因此这也应该具有可比性).

就清晰度而言,它最终归结为品味问题。 我个人喜欢Any()的表现力,因为它对我来说更接近自然语言。

rows.Any()

让我想不到

rows.Count == 0

但不是那么多。

null比较以完全不同的方式工作。 如果您不清楚这一点,您应该在 C# 中了解值和引用类型(警告:最近情况有所不同,因为在当前版本的 C#/.NET 中默认值类型不允许使用null )。

总结:引用类型的存储方式不同,实际变量(某种程度上)只是指向数据存储位置的指针(不是 100% 准确)。 C++ 中的经典指针可以采用值0来指示没有它们指向的数据,这被转移(至少作为一个概念)到 C#(以及其他类似 C 的语言,如 Java)。 无论如何,由于在 C# 中不直接访问指针,因此发明了null以指示该变量不指向实际数据(“没有与该变量关联的数据”)。 因此,将List<int>变量与null进行比较意味着您询问列表实例是否已经创建,这是一个必要条件,但对于存储的实际列表项来说并不是一个充分的条件。

这取决于你的目标。 List可以不是 null,但Count == 0 Any()怎么样 - 在 LINQ 风格中使用它,例如:

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

检查列表是否为 null。这可能代表也可能不代表“无项目”。 我建议完全避免将 null 用于列表,因为含义不明确。 使用空集合来表示“没有项目”。 如果您需要其他东西来表示“不存在”,请使用可能/可选类型,或者如果默认情况下它们不可为空,则使用可能为空的引用。 这应该向读者发出更明确的信号,即“没有项目”和“不存在”是可能需要以不同方式处理的不同状态。

.Any()

这是 LINQ 的一部分,因此适用于任何可枚举的集合。 非常清晰方便,但会比查属性稍慢。 但是如果你想检查某个特定值的存在,它允许内联过滤。

.Count > 0 or .Length > 0

这仅适用于具有计数/长度属性的 collections,但应该是最快的,因为它只是进行比较。 至少假设属性很简单并且不会做很多工作。

但请注意,除非您非常频繁地运行代码,否则性能问题并不是很重要。 因此,在尝试进行任何优化之前,先测量性能。

TL;DR: Any()实际上会调用ICollection.Count ,如果集合类型实现的话。 否则它将尝试选择“最佳”方法来检查集合是否为非空。

检查null与检查空值有很大不同,将它与空值检查进行比较并没有什么意义。


Paul Kertscher 已经回答了您的问题的实质,但是为了帮助将来查询此类问题,值得记住的是,do.net 的源代码实际上可以在线获得。

例如,LINQ Any()方法的代码可以在这里找到: https://github.com/do.net/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/ AnyAll.cs (运行时存储库的 rest 中还有更多内容,库在这里)。 它比 Paul 在他的回答中包含的方法版本稍微复杂一些……他可能引用了不同版本的运行时。 不过,这是最新的实现(原始来源中的评论):

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();
    }
}

ICollection的代码是System.Collection的一部分,而IListProviderSystem.Linq的一部分。

您可以看到Any()可以通过多种方式来测试底层集合中是否包含任何项目,并且您可以合理地假设它们是按照速度/效率的顺序进行尝试的。 显然,您可以编写一个非常棒的ICollection.Count { get }实现,但这将是一件愚蠢的事情,并且很高兴认为微软没有在 do 附带的任何集合类型中这样做.net 运行时。 (顺便说一句, arrays通过显式接口实现ICollection.Count ,将其映射到它们的Length属性,这就是Any()不需要关心检查Length的原因)

我还没有查找IListProvider<T>.GetCount的任何实现。 接口方法的文档是这样说的:

如果为真,则只有在快速计算(确定或可能是恒定时间)时才应计算计数,否则应返回 -1。

同样,糟糕的实现可能存在,但可能不存在。

使用Any()而不是Count通常没有错。 调用方法和运行一些 ifs 和强制转换的额外开销通常可以忽略不计。 只需确保不要将CountCount()混淆,因为后者可能需要遍历整个集合,这可能是一件相对昂贵且耗时的事情,如果您只想检查空虚。

暂无
暂无

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

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