Linq Any()的副作用[英] Linq Any() side effects

本文是小编为大家收集整理的关于Linq Any()的副作用的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

如果反射器是正确的(我倾向于相信它是正确的),这就是 Any() 的实现:

public static bool Any<TSource>(this IEnumerable<TSource> source) {
        if (source == null) throw Error.ArgumentNull("source");
        using (IEnumerator<TSource> e = source.GetEnumerator()) {
            if (e.MoveNext()) return true;
        }
        return false;
    }

据我了解,MoveNext() 将底层枚举器移动了一个位置,因此,多次调用 Any() 会对"缩小"集合产生不利影响.

我尝试使用 List<> 重现此问题,但无法执行此操作,但我无法找出 List<> 解决此问题的不同之处.

验证 List<T> 与多个 Any() 调用一起正常工作的简单示例:

var meh = new List<string> {"1", "2"};
var enumerable = meh.AsEnumerable();
bool any = enumerable.Any(); //true
any = enumerable.Any(); //true
any = enumerable.Any(); //true but isn't it meant to have "moved" the enumerator by two positions already?
any = enumerable.Any(); //true

所以我的问题是:

  1. 我是否正确理解 Any() 确实对 Enumerable 有副作用
  2. 如果是,List<>如何规避?

对不起,如果这是一个愚蠢的问题.只是我发现非常有趣的东西.

推荐答案

由于 using 语句,枚举数被释放,所以它总是从头开始.枚举器也总是从 source.GetEnumerator() 创建并且不被重用.实际上这是 .NET 源代码,您可以在 这里看到.

Any:

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (predicate(element)) return true;
    }
    return false;
}

它会枚举序列直到谓词匹配,然后将其释放.

这个方法也没有使用延迟执行(它缺少 yield 关键字).因此它总是立即执行.

本文地址:https://www.itbaoku.cn/post/1556964.html

问题描述

If reflector is right (and I tend to believe it is), this is the implementation for Any():

public static bool Any<TSource>(this IEnumerable<TSource> source) {
        if (source == null) throw Error.ArgumentNull("source");
        using (IEnumerator<TSource> e = source.GetEnumerator()) {
            if (e.MoveNext()) return true;
        }
        return false;
    }

From my understanding, MoveNext() moves the underlying enumerator by one position and therefore, calling Any() multiple times can have an adverse effect on "shrinking" the collection.

I tried to reproduce this with a List<> but I wasn't able to do this, yet, I'm unable to find out what List<> does differently to address this problem.

My simple example to verify List<T> works correctly with multiple Any() calls:

var meh = new List<string> {"1", "2"};
var enumerable = meh.AsEnumerable();
bool any = enumerable.Any(); //true
any = enumerable.Any(); //true
any = enumerable.Any(); //true but isn't it meant to have "moved" the enumerator by two positions already?
any = enumerable.Any(); //true

So my questions are:

  1. Am I correct in understanding that Any() does indeed have side effects on the Enumerable
  2. If so, how does List<> circumvent it?

Sorry if it's a dumb question in advance. Just something that I found very interesting.

推荐答案

Due to the using statement the enumerator is disposed, so it always starts at the beginning. The enumerator is also always created from source.GetEnumerator() and not reused. Actually this is the .NET source as you can see here.

The same is true for the other overload of Any:

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (predicate(element)) return true;
    }
    return false;
}

It enumerates the sequence until the predicate matches, then it will be disposed.

This method is also not using deferred execution(it lacks the yield keyword). Therefore it is always executed immediately.