开发者

C# - Why implement two version of Current when realizing IEnumerable Interface?

I assume the following sample gives a best practice that we should follow when we implement the IEnumerable interface.

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

Here is the question:

  1. Why should we provide two version of Current method?
  2. When the version ONE (object IEnumerator.Current) is used?
  3. When the version TWO (public Person Current ) is used?
  4. How to use PeopleEnum in the foreach statement. // updated
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();
            }
        }
    }
}


The IEnumerator.Current is an explicit interface implementation.

You can only use it if you cast the iterator to an IEnumerator (which is what the framework does with foreach). In other cases, the second version will be used.

You will see that it returns object and actually uses the other implementation which returns a Person.

The second implementation is not required per se by the interface, but is there as a convenience and in order to return the expected type instead of object.


Long-form implementation of IEnumerator is no longer necessary:

public class PeopleEnum : IEnumerable
{
    public Person[] _people;

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

    public IEnumerator GetEnumerator()
    {
        foreach (Person person in _people)
            yield return person;
    }
}

And to further bring it into the 21st century, don't use the non-generic 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();
    }
}


I suspect the reason is that this code example was derived from an example class implementing IEnumerator<T> - if the example class PeopleEnum implemented IEnumerator<T> this approach would be required: IEnumerator<T> inherits IEnumerator so you have to implement both interfaces when implementing IEnumerator<T>.

The implementation of the non-generic IEnumerator requires Current to return object - the strongly typed IEnumerator<T> on the other hand requires Current to return an instance of type T - using explicit and direct interface implementation is the only way to fulfill both requirements.


It is there for convenience, eg. using the PeopleEnum.Current in a typesafe way in a while(p.MoveNext()) loop, not explicitly doing a foreach enumeration.

But the only thing you need to do is implement the interface, you could do it implicitly if you wish, however is there a reason for it? If I wanted to use MovePrevious on the class? Would it be cool if I should cast(unbox) the object to Person?

If you think the class could be extended with more manipulation methods the Person Current is a cool thing.


Version two isnt part of the interface. You have to satisfy the interface requirements.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜