[英]C# Best Practices when using !=null, Count > 0, and .Any()
出于好奇, !=null
、 Count > 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>.Count
( IEnumerable
扩展试图回退到该值,因此这也应该具有可比性).
就清晰度而言,它最终归结为品味问题。 我个人喜欢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的一部分,而IListProvider
是System.Linq的一部分。
您可以看到Any()
可以通过多种方式来测试底层集合中是否包含任何项目,并且您可以合理地假设它们是按照速度/效率的顺序进行尝试的。 显然,您可以编写一个非常棒的ICollection.Count { get }
实现,但这将是一件愚蠢的事情,并且很高兴认为微软没有在 do 附带的任何集合类型中这样做.net 运行时。 (顺便说一句, arrays通过显式接口实现ICollection.Count
,将其映射到它们的Length
属性,这就是Any()
不需要关心检查Length
的原因)
我还没有查找IListProvider<T>.GetCount
的任何实现。 接口方法的文档是这样说的:
如果为真,则只有在快速计算(确定或可能是恒定时间)时才应计算计数,否则应返回 -1。
同样,糟糕的实现可能存在,但可能不存在。
使用Any()
而不是Count
通常没有错。 调用方法和运行一些 ifs 和强制转换的额外开销通常可以忽略不计。 只需确保不要将Count
与Count()
混淆,因为后者可能需要遍历整个集合,这可能是一件相对昂贵且耗时的事情,如果您只想检查空虚。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.