在 C# 中,当输入方法/本地函数时,我的 IEnumerator 会被重置/重新创建。为什么?

rab*_*ens 3 c# ienumerator enumerator

考虑这个例子:

List<int> l = [1, 2, 3, 4, 5];
void DoSomethingWith(IEnumerator<int> e) {
    Console.WriteLine(e.MoveNext());
    Console.WriteLine(e.Current);
}
using(var e = l.GetEnumerator()) {
    DoSomethingWith(e);
    DoSomethingWith(e);
}
Run Code Online (Sandbox Code Playgroud)

该片段输出:

True
1
True
1
Run Code Online (Sandbox Code Playgroud)

我预计

True
1
True
2
Run Code Online (Sandbox Code Playgroud)

因为我传递了同一个枚举器。

为什么会这样呢?

Gur*_*ron 9

List<T>.Enumerator返回的List<T>.GetEnumerator是 a,struct因此通过接口传递它会将其装箱(因此后续调用将使用从当前状态复制的不同实例)。例如以下内容:

using(var e = l.GetEnumerator())
{
    e.MoveNext();
    DoSomethingWith(e);
    DoSomethingWith(e);
}
Run Code Online (Sandbox Code Playgroud)

将打印:

True
2   
True
2   
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,原始枚举器实际上并未重置。

您可以在 as 接口中显式指定类型using(这样您将装箱一次并传递枚举器的同一实例):

True
2   
True
2   
Run Code Online (Sandbox Code Playgroud)

或者更改方法以接受List<int>.Enumerator并传递它ref

using(IEnumerator<int> e = l.GetEnumerator())
{
    e.MoveNext();
    DoSomethingWith(e);
    DoSomethingWith(e);
}
Run Code Online (Sandbox Code Playgroud)

或者通过接口使用集合,因为IEnumerable<T>.GetEnumerator()是显式实现的,并且装箱将由实现处理:

void DoSomethingWith(ref List<int>.Enumerator e)
{
    Console.WriteLine(e.MoveNext());
    Console.WriteLine(e.Current);
}

var e = l.GetEnumerator();
DoSomethingWith(ref e);
DoSomethingWith(ref e);
Run Code Online (Sandbox Code Playgroud)