已经有一个question on SO about "possible multiple enumerations",但是这个问题更具体。
请考虑以下方法,该方法将IEnumerable<string>
作为输入并针对其每个元素执行给定方法:
public static bool SomeMethod(IEnumerable<string> enumerable)
{
if (enumerable.IsNullOrEmpty())
{
// throw exception.
}
else
{
return (enumerable.All(SomeBooleanMethod));
}
}
在上面的代码中,IsNullOrEmpty
只是运行的扩展方法
return (!ReferenceEquals(enumerable, null) || enumerable.Any());
问题是ReSharper向我警告“可能存在IEnumerable的多个枚举”,我真的不知道这是否可能是一个问题。
我理解警告的含义,但是如果您真的需要检查并抛出异常(如果为空或为空的话),您在这种情况下会真正地做什么?
这意味着您(部分)对IEnumerable进行了多次迭代:首先在对Any()
的调用中(它至少需要初始化一个迭代以查看可枚举是否返回任何元素),而第二次是All
(从头开始迭代)。
ReSharper对此警告您的原因是,枚举一个枚举可能会导致副作用,而无意地重复两次可能会触发两次副作用,这可能是不希望的,或者是不希望的。
正如@tdammers所标识的,所指的“多个枚举”是Any
和All
所需的两个枚举。由于您想拒绝一个空序列,因此我能想到的最好的方法是:
public static bool SomeMethod(IEnumerable<string> enumerable)
{
if (enumerable == null)
throw new ArgumentNullException();
// Manually perform an All, keeping track of if there are any elements
bool anyElements = false;
bool result = true;
foreach (string item in enumerable)
{
anyElements = true;
result = result && SomeBooleanMethod(item);
// Can short-circuit here
if (!result)
break;
}
if (!anyElements)
throw new ArgumentException(); // Empty sequence is invalid argument
return result;
}
虽然这里的其他答案是正确的,因为您要枚举两次(以及这样做的潜在危害),但是对于您得到警告的原因,它们都(有点)不正确。
Resharper不会警告您,因为您正在呼叫Any()
和All()
。这是警告您,因为您正在调用IsNullOrEmpty()
和All()
。实际上,Resharper甚至不认识您are呼叫Any()
。尝试将其删除-您会发现您仍然收到警告。
这是因为Resharper有no idea传递给另一个方法的可枚举发生了什么。也许其他方法枚举了它,也许不是。但是您将枚举方法传递给了两种方法,因此也许它们都枚举了它,所以也许它被枚举了两次。因此,警告为“ << [可能多重枚举”。
这很微妙,但很重要。在您的情况下,该警告很有用,因为您被
枚举两次。但是也许您的扩展方法没有枚举可枚举,并且您know警告可以忽略。在这种情况下,Resharper为您提供NoEnumeration
attribute in Resharper's Code Annotations。这使您可以标记枚举,如下面的组合方法所示:public static bool IsNull<T>([NoEnumeration]this IEnumerable<T> enumerable)
{
return enumerable is null;
}