[英]Enumerable.Any() and possible multiple enumerations
Rider/ReSharper 给了我可能的多重枚举警告:
public void ProcessProductCodes(IEnumerable<string> productCodes) {
if (productCodes.Any()) {
DoStuff(productCodes);
}
}
是误报,还是 Any() function 确实弄乱了集合的枚举?
IEnumerable
接口表示可以迭代的项目序列,但不对序列的来源做任何假设。 例如,它可以是数据库查询。 如果是这种情况,您将对数据库进行 2 次调用,一次用于检查序列中是否有任何项目,另一次用于将它们传递给DoStuff
function,这显然不是最佳性能,而 ReSharper 是警告你这一点。
为避免此问题,您有 2 种不同的选择。 如果项目集合已经在 memory 中,您可以通过将 function 的签名更改为:
public void ProcessProductCodes(ICollection<string> productCodes) { ... }
如果您不能保证这一点,您可以在 function 的开头执行.ToList()
或.ToArray
:
public void ProcessProductCodes(IEnumerable<string> productCodes) {
var productCodesList = productCodes.ToList();
if (productCodesList .Any()) {
DoStuff(productCodesList );
}
ReSharper 将为您执行此操作,只需 select 快速重构(通常使用Alt+Enter
)。
您可以创建一个辅助方法(我已将其创建为扩展方法)以确保它只迭代一次:
public static class LinqExtensions
{
public static bool DoIfAny<T>(this IEnumerable<T> collection, Action<IEnumerable<T>> action)
{
var enumerator = collection.GetEnumerator();
if (!enumerator.MoveNext())
{
return false;
}
action(CreateEnumerableFromStartedEnumerable(enumerator));
return true;
}
private static IEnumerable<T> CreateEnumerableFromStartedEnumerable<T>(IEnumerator<T> enumerator)
{
do
{
yield return enumerator.Current;
}
while (enumerator.MoveNext());
}
}
本质上,这将为集合创建一个枚举器,然后尝试移动到第一项。 如果失败,则不调用该方法并返回false
。
如果它成功,那么它将产生一个新的枚举,它迭代源枚举的 rest,并在运行时产生它的值。 这包括第一个值。 然后将其传递给操作委托并返回true
。
用法:
IEnumerable<string> values = new string[] { "a", "b", "c" };
bool delegateCalled = values.DoIfAny(e => DoStuff(e));
Console.WriteLine("Delegate called: " + delegateCalled.ToString());
请注意,只有在“集合不为空”的意义上想要.Any()
时,这才会真正起作用。 如果它正在检查特定元素的存在,那么您必须首先将列表具体化,如 Tao 的回答。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.