列表.Enumerator IEnumerator.Reset() 方法实现

本文关键字:方法 实现 Reset IEnumerator Enumerator 列表 | 更新日期: 2023-09-27 18:33:17

尽管事实上,IEnumerator.Reset方法永远不应该使用,但我在List<T>中发现了方法实现的奇怪行为。

无论您如何检查 .NET Framework 源代码(使用引用源和 ILSpy 尝试),该方法都按如下方式实现:

void System.Collections.IEnumerator.Reset() {
    if (version != list._version) {
        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
    }
    index = 0;
    current = default(T);
}

但是,看起来根本没有调用该方法!考虑代码:

var list = new List<int>(1) { 3 };
using (var e = list.GetEnumerator())
{
    Console.WriteLine(e.MoveNext());
    Console.WriteLine(e.Current);
    ((IEnumerator)e).Reset();
    Console.WriteLine(e.MoveNext());
    Console.WriteLine(e.Current);
}

很明显,它应该打印True3两次。取而代之的是,结果是

True
3
False
0

我缺少任何简单的解释?

列表<T>.Enumerator IEnumerator.Reset() 方法实现

我缺少任何简单的解释?

是的:你在这里装箱List.Enumerator

((IEnumerator)e).Reset();

这会获取现有副本的副本并重置它 - 将原始副本留在一块中。

若要重置实际枚举器,需要如下所示的内容:

var list = new List<int>(1) { 3 };
var e = list.GetEnumerator();
// Can't use "ref" with a using statement
try
{
    Console.WriteLine(e.MoveNext());
    Console.WriteLine(e.Current);
    Reset(ref e);
    Console.WriteLine(e.MoveNext());
    Console.WriteLine(e.Current);
}
finally
{
    e.Dispose();
}
static void Reset<T>(ref T enumerator) where T : IEnumerator
{
    enumerator.Reset();
}

这很棘手,因为它使用显式接口实现。

我还没有测试过它,但我认为这应该适合你。显然,这样做是个坏主意...

编辑:或者,只需将变量类型更改为IEnumeratorIEnumerator<int>即可开始。然后它将被装箱一次Reset 方法将改变装箱值:

var list = new List<int>(1) { 3 };
using (IEnumerator e = list.GetEnumerator())
{
    Console.WriteLine(e.MoveNext());
    Console.WriteLine(e.Current);
    e.Reset();
    Console.WriteLine(e.MoveNext());
    Console.WriteLine(e.Current);
}