甚至“IsNullOrEmpty"检查给出了“ienumerable的可能的多个枚举”;警告

本文关键字:枚举 警告 quot IsNullOrEmpty 检查 甚至 ienumerable | 更新日期: 2023-09-27 18:06:57

在SO上已经有一个关于"可能的多个枚举"的问题,但这个问题更具体。

请考虑以下方法,该方法将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异常,那么您真正可以做什么呢?

甚至“IsNullOrEmpty"检查给出了“ienumerable的可能的多个枚举”;警告

这意味着您(部分地)在IEnumerable上迭代不止一次:第一次是在调用Any()时(至少需要初始化迭代以查看enumerable是否返回任何元素),第二次是在All中(从开始迭代)。

ReSharper警告你这一点的原因是枚举一个可枚举对象可能会导致副作用,而无意中迭代两次可能会触发两次副作用,这可能是也可能不是我们想要的。

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

虽然这里的其他答案都是正确的,因为您枚举了两次(以及这样做的潜在危害),但它们都(微妙地)不正确,因为您为什么会得到警告。

Resharper没有警告你,因为你正在调用Any()All()。它警告你,因为你正在调用IsNullOrEmpty()All()。事实上,Resharper甚至不知道你正在调用Any() .试着删除它-你会发现你仍然得到警告。

这是因为Resharper 不知道传递给另一个方法的可枚举对象会发生什么。也许其他方法枚举了它,也许没有。但是你将可枚举对象传递给两个方法,所以它们可能都枚举它,所以它可能被枚举两次。因此出现了警告,"可能存在多个枚举"。

这很微妙但很重要。在您的例子中,警告是有用的,因为您的枚举了两次。但是,也许您的扩展方法没有枚举可枚举对象,并且您知道可以忽略警告。在这种情况下,Resharper提供了NoEnumeration属性在Resharper的代码注释。

这允许你标记一个可枚举对象,就像下面的make up方法:

public static bool IsNull<T>([NoEnumeration]this IEnumerable<T> enumerable)
{
    return enumerable is null;
}