【问题标题】:How Can I Use IEnumerator.Reset()?如何使用 IEnumerator.Reset()?
【发布时间】:2011-08-23 13:24:19
【问题描述】:

调用IEnumerator.Reset的正确方法是什么?

文档说:

Reset 方法用于 COM 互操作性。它不一定需要实施;相反,实现者可以简单地抛出一个NotSupportedException

好的,这是否意味着我不应该调用它?

如此很想使用异常来进行流控制:

using (enumerator = GetSomeExpensiveEnumerator())
{
    while (enumerator.MoveNext()) { ... }

    try { enumerator.Reset(); } //Try an inexpensive method
    catch (NotSupportedException)
    { enumerator = GetSomeExpensiveEnumerator(); } //Fine, get another one

    while (enumerator.MoveNext()) { ... }
}

这就是我们应该使用它的方式吗?或者我们根本不打算从托管代码中使用它?

【问题讨论】:

  • 请永远不要使用异常来控制流...

标签: c# .net ienumerator


【解决方案1】:

从不;最终这是一个错误。多次迭代序列的正确方法是再次调用.GetEnumerator() - 即再次使用foreach。如果您的数据不可重复(或重复成本高昂),请通过.ToList() 或类似方式对其进行缓冲。

迭代器块为此方法抛出异常是语言规范中的正式要求。因此,您不能依赖它工作。永远。

【讨论】:

  • +1 我不知道这部分是语言规范中的要求......那么是的,这绝对是一个错误。有点无赖哈哈,有时调用它要容易得多。
  • @Mark Gravell:并非所有枚举器都是迭代器块。如果您使用已知的 IEnumerator 实现,其 Reset 方法已知可以工作,则可以依赖 Reset 工作。例如,System.Collections.Generic.List.
  • @phoog 是否明确记录可以工作...?然后你将自己限制在List<T>,而不是界面——这有点痛苦。如果您已经知道您有 list-of-T,那么您也知道迭代它两次同样简单......
  • 是的,它被明确记录在案 (msdn.microsoft.com/en-us/library/bb335884.aspx)。我只是选择 List 作为支持该方法的库类型的快速示例。
  • @springy76 现实是“大多数”根本不够有用;它要么是合同的有用部分,要么不是。由于无法测试它是否受支持(例如bool ResetSupported {get;}),它是无用的。 IIRC,编译器规范实际上要求迭代器块在调用Reset()时抛出Reset()
【解决方案2】:

我建议不要使用它。许多现代的IEnumerable 实现只会抛出异常。

获取枚举器几乎不会“昂贵”。它正在(完全)枚举它们,这可能很昂贵。

【讨论】:

  • 不过,根据问题,它可能是昂贵的,至少相对于其他正常操作而言。例如:假设您正在包装对FindFirstFile/FindNextFile 的调用——这些调用获取系统句柄,与数组枚举之类的操作相比,这是一个相对昂贵的操作。
  • @MMehrdad: 但随后获取数组once,并对其进行迭代。当你需要重新启动时,为你已经拥有的同一个数组获取一个新的迭代器。
  • @Mehrdad:在这种情况下,定义枚举器,使其仅在枚举第一项时调用FindFirstFile。这样一来,它的作用就和其他所有枚举器一样。
  • @Mehrdad:如果是这样呢?听起来唯一能满足您要求的解决方案是 magic。不可能同时拥有它。您可以要么将数组保存在内存中,从而降低一次又一次迭代的成本,或者您可以按需加载每个元素,从而降低重新开始的成本。无论这两个选项如何包装,它们仍然是你所拥有的。如果Reset() 可以安全使用,那么它仍然需要做这两个之一
  • 枚举器可以将整个数组存储在内存中,或者当你调用Next时它可以调用FindNextFile,如果你调用Reset,它可以调用FindFirstFile。没有其他选择。
【解决方案3】:
public class PeopleEnum : IEnumerator
{
    public Person[] _people;

    // Enumerators are positioned before the first element 
    // until the first MoveNext() call. 
    int position = -1;

    public PeopleEnum(Person[] list)
    {
        _people = list;
    }

    public bool MoveNext()
    {
        position++;
        return (position < _people.Length);
    }

    public void Reset()
    {
        position = -1;
    }

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }

    public Person Current
    {
        get
        {
            try
            {
                return _people[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2011-04-26
    • 2013-10-06
    • 1970-01-01
    • 1970-01-01
    • 2018-07-29
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    相关资源
    最近更新 更多