【问题标题】:List<T>.Enumerator IEnumerator.Reset() method implementationList<T>.Enumerator IEnumerator.Reset() 方法实现
【发布时间】:2013-10-06 16:37:07
【问题描述】:

尽管IEnumerator.Reset method should never be used 我发现List&lt;T&gt; 中方法实现的奇怪行为。

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

我缺少任何简单的解释吗?

【问题讨论】:

  • 原来是为什么可变结构是邪恶的一个新例子。通过显式接口实现发生突变的可变结构只是有点额外的邪恶。但是这些嵌套的 Enumerator 结构通常仅在 C# 编译器翻译 foreach 语句时使用。

标签: c# list ienumerator


【解决方案1】:

我缺少任何简单的解释吗?

是的:你在这里打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&lt;int&gt; 即可。然后它会被装箱一次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);
}

【讨论】:

  • 您还需要将其从using 中取出以通过引用传递。除了那个变化,我很快测试了它,这确实有效。
  • @Servy 是对的。他可以简单地在开始时将它装箱,然后一直重复使用同一个盒子。编辑:例如,如果他首先将List&lt;&gt; 向上转换为接口类型IEnumerable&lt;&gt;IEnumerable,然后使用该接口的GetEnumerator() 方法,则枚举器在获取时已经在一个框中,他可以重新- 在整个示例中使用该框。
  • 当然,你是对的。我刚刚通过将IEnumerator 分配给变量对其进行了测试,它实际上已被重置。谢谢。我会尽快将答案标记为已接受。
  • @JeppeStigNielsen:是的,我在离开家的时候就想到了这一点 :) 将编辑它。
猜你喜欢
  • 2012-05-26
  • 2014-06-02
  • 1970-01-01
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
  • 2011-10-09
  • 1970-01-01
  • 2021-08-23
相关资源
最近更新 更多