【问题标题】:C# - Why implement two version of Current when realizing IEnumerable Interface?C# - 实现 IEnumerable 接口时为什么要实现两个版本的 Current?
【发布时间】:2019-07-09 23:45:03
【问题描述】:

我假设以下示例提供了我们在实现 IEnumerable 接口时应该遵循的最佳实践。

https://docs.microsoft.com/en-us/dotnet/api/system.collections.ienumerator.movenext

问题来了:

  1. 为什么要提供两个版本的Current方法?
  2. 何时使用版本 ONE(对象 IEnumerator.Current)?
  3. 什么时候使用版本二(public Person Current)?
  4. 如何在 foreach 语句中使用 PeopleEnum。 // 更新了
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;
    }

    // explicit interface implementation
    object IEnumerator.Current /// **version ONE**
    {
        get
        {
            return Current;
        }
    }

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

【问题讨论】:

    标签: c#


    【解决方案1】:

    IEnumerator.Current 是一个显式接口实现。

    只有将迭代器转换为IEnumerator(这是框架对foreach 所做的),才能使用它。在其他情况下,将使用第二个版本。

    您会看到它返回object,实际上使用另一个返回Person的实现。

    接口本身不需要第二个实现,但它是为了方便并返回预期的类型而不是object

    【讨论】:

    • 尽管所有这些都是正确的,但您推断 IEnumerator 的强制转换是罕见的;不是。 IEnumerable 需要一个 GetEnumerator() 方法,该方法返回一个 IEnumerator 类型的对象,按原样进行强制转换。反过来,几乎所有使用 IEnumerables 的内置代码都使用 GetEnumerator(),例如 foreach 和大部分 Linq。
    【解决方案2】:

    不再需要 IEnumerator 的长格式实现:

    public class PeopleEnum : IEnumerable
    {
        public Person[] _people;
    
        public PeopleEnum(Person[] list)
        {
            _people = list;
        }
    
        public IEnumerator GetEnumerator()
        {
            foreach (Person person in _people)
                yield return person;
        }
    }
    

    为了进一步将其带入 21 世纪,请不要使用非泛型 IEnumerable:

    public class PeopleEnum : IEnumerable<Person>
    {
        public Person[] _people;
    
        public PeopleEnum(Person[] list)
        {
            _people = list;
        }
    
        public IEnumerator<Person> GetEnumerator()
        {
            foreach (Person person in _people)
                yield return person;
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
    

    【讨论】:

      【解决方案3】:

      我怀疑原因是这个代码示例派生自实现IEnumerator&lt;T&gt; 的示例类 - 如果示例类PeopleEnum 实现IEnumerator&lt;T&gt; 需要这种方法:IEnumerator&lt;T&gt; 继承IEnumerator 所以你有在实现IEnumerator&lt;T&gt;时实现两个接口。

      非泛型IEnumerator 的实现需要Current 返回对象——另一方面,强类型IEnumerator&lt;T&gt; 需要Current 返回T 类型的实例——使用显式和直接接口实现是满足这两个要求的唯一方法。

      【讨论】:

      • 我刚刚发现您实际上给出了我需要的答案。 ——谢谢
      【解决方案4】:

      为了方便起见,例如。在 while(p.MoveNext()) 循环中以类型安全的方式使用 PeopleEnum.Current,而不是显式地进行 foreach 枚举。

      但是你唯一需要做的就是实现接口,如果你愿意,你可以隐式地做,但是有什么理由吗?如果我想在课堂上使用 MovePrevious?如果我应该将对象强制转换(取消装箱)到 Person 会不会很酷?

      如果您认为可以使用更多操作方法扩展该类,那么 Person Current 是一件很酷的事情。

      【讨论】:

        【解决方案5】:

        第二版不是界面的一部分。您必须满足接口要求。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-12-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-12
          • 2011-05-05
          • 2010-09-29
          相关资源
          最近更新 更多