甚至“IsNullOrEmpty”检查也会给出“可能多次枚举IEnumerable”的警告

时间:2011-08-30 13:44:33

标签: c# resharper ienumerable

已经有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的可能的多个枚举”,我真的不知道这是否真的是一个问题。

我理解警告的含义,但是如果你确实需要在无效或空洞的情况下检查并抛出异常,你能在这种情况下做些什么呢?

3 个答案:

答案 0 :(得分:30)

这意味着您(部分)迭代IEnumerable多次:首先调用Any()(需要至少初始化迭代以查看枚举是否返回任何元素),以及All中的第二次(从头开始迭代)。

ReSharper警告你的原因是枚举可枚举可能会引起副作用,无意中迭代两次可能会引发副作用两次,这可能是也可能不是。

答案 1 :(得分:8)

正如@tdammers所识别的那样,所引用的“多个枚举”是AnyAll所需的两个枚举。由于你想要拒绝一个空序列,我能想到的最好的是:

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