List .Enumerator IEnumerator.Reset()方法实现

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

无论您如何检查.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(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 

我错过了任何简单的解释?

我错过了任何简单的解释?

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

 ((IEnumerator)e).Reset(); 

这需要现有的副本并重置它 – 将原件保留为一件。

要重置实际的枚举器,你需要这样的东西:

 var list = new List(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(ref T enumerator) where T : IEnumerator { enumerator.Reset(); } 

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

我没有测试过,但我认为这对你有用。 显然这样做是个坏主意……

编辑:或者,只需将您的变量类型更改为IEnumeratorIEnumerator即可。 然后它将被装箱一次 ,并且Reset方法将改变盒装值:

 var list = new List(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); }