已经有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的可能的多个枚举”,我真的不知道这是否真的是一个问题。
我理解警告的含义,但是如果你确实需要在无效或空洞的情况下检查并抛出异常,你能在这种情况下做些什么呢?
答案 0 :(得分:30)
这意味着您(部分)迭代IEnumerable多次:首先调用Any()
(需要至少初始化迭代以查看枚举是否返回任何元素),以及All
中的第二次(从头开始迭代)。
ReSharper警告你的原因是枚举可枚举可能会引起副作用,无意中迭代两次可能会引发副作用两次,这可能是也可能不是。
答案 1 :(得分:8)
正如@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;
}
答案 2 :(得分:0)
虽然这里的其他答案是正确的,因为您要枚举两次(以及这样做的潜在危害),但是它们在(为什么)得到警告的原因上都是(微妙的)错误。
Resharper不会警告您,因为您正在呼叫Any()
和All()
。这是警告您,因为您正在呼叫IsNullOrEmpty()
和All()
。实际上,Resharper甚至不知道您是正在呼叫Any()
。尝试将其删除-您会发现您仍然收到警告。
这是因为Resharper不知道 会如何将枚举传递给另一方法。也许其他方法枚举了它,也许不是。但是您将枚举方法传递给了两种方法,因此也许它们都枚举了它,所以也许它被枚举了两次。因此,警告为“ 可能多重枚举”。
这很微妙但很重要。对于您而言,该警告很有用,因为您被枚举了两次。但是也许您的扩展方法没有枚举可枚举,并且您知道该警告可以忽略。在这种情况下,Resharper为您提供了NoEnumeration
attribute in Resharper's Code Annotations。
这使您可以标记枚举,如下面的组合方法所示:
public static bool IsNull<T>([NoEnumeration]this IEnumerable<T> enumerable)
{
return enumerable is null;
}