【问题标题】:Even "IsNullOrEmpty" checks give "Possible multiple enumeration of IEnumerable" warnings甚至“IsNullOrEmpty”检查也会给出“IEnumerable 的可能多重枚举”警告
【发布时间】:2011-08-30 13:44:33
【问题描述】:

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

我理解警告的含义,但是如果你真的需要检查并抛出异常以防 null 或 empty,你能做些什么呢?

【问题讨论】:

  • 如果传递一个空序列,为什么要让这个方法抛出? “对这个(空)序列的每个成员做某事”的语义非常明显,不是吗?另外,您使用ReferenceEquals() 而不仅仅是== null 有什么特别的原因吗?
  • 确实不是,但现在想象这是一个构造函数。如果我不能从一个空序列构造一个对象,我不应该抛出异常吗?
  • 当然,如果你真的无能为力;但一般我会说空序列应该被视为与任何非空序列一样好。例如,List&lt;&gt; 非常乐意从空序列构造。当然,我只是泛泛而谈;你知道你的情况的细节

标签: c# resharper ienumerable


【解决方案1】:

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

ReSharper 警告您这一点的原因是,枚举一个可枚举对象可能会导致副作用,并且无意中迭代两次可能会触发两次副作用,这可能是可取的,也可能不是可取的。

【讨论】:

  • 能否请您解释一下副作用或任何例子都会令人愉快。
  • 您的枚举可以,例如,从网络流中读取字节(这将构成副作用)。如果您枚举一次,一切都很好,并且可以一口气从头到尾读取流。但是,如果您中止第一次迭代,然后再次迭代,即使底层网络流无法向后搜索,您很可能会得到不希望的行为。
  • 值得注意的是,Resharper 不知道对Any() 的调用。它所知道的是SomeMethod() 将枚举传递给另外两个方法(IsNullOrEmpty()All()),它们可能会或可能不会枚举它。
【解决方案2】:

正如@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;
}

【讨论】:

  • 所指的可能多个枚举不是AnyAll。他们是IsNullOrEmptyAll
【解决方案3】:

虽然这里的其他答案是正确的,因为您列举了两次(以及这样做的潜在危害),但对于您收到警告的原因,它们都(略微)不正确。

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

【讨论】:

    猜你喜欢
    • 2012-01-04
    • 1970-01-01
    • 2012-02-12
    • 1970-01-01
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多