views:

136

answers:

2

Why in this example from MSDN, in GetEnumerator method, PeopleEnum returns IEnumerator?

public class Person
{
    public Person(string fName, string lName)
    {
        this.firstName = fName;
        this.lastName = lName;
    }

    public string firstName;
    public string lastName;
}

public class People : IEnumerable
{
    private Person[] _people;
    public People(Person[] pArray)
    {
        _people = new Person[pArray.Length];

        for (int i = 0; i < pArray.Length; i++)
        {
            _people[i] = pArray[i];
        }
    }
   //why??? 
   IEnumerator IEnumerable.GetEnumerator()
   {
       return (IEnumerator) GetEnumerator();
   }

   public PeopleEnum GetEnumerator()
   {
       return new PeopleEnum(_people);
   }
}

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();
        }
    }
}

UPDATE: BTW, if Array data type implements ICloneable interface, why msdn has copied pArray to _people by writing a for loop?

+3  A: 

It needs to return exactly IEnumerator to properly implement the IEnumerable interface. It is doing this using an "explicit interface implementation", so on the public API you see PeopleEnum, but IEnumerable is still happy

But in reality you would very rarely write an enumerator this way in C# 2.0 or above; you'd use an iterator block (yield return). See C# in Depth chapter 6 (free chapter!).

For info, the reason that PeopleEnum exists at all here is that this looks like a .NET 1.1 sample, where that is the only way to create a typed enumerator. In .NET 2.0 and above there is IEnumerable<T> / IEnumerator<T>, which has a typed (via generics) .Current.

In .NET 2.0 / C# 2.0 (or above) I would have simply:

public class People : IEnumerable<Person> {
    /* snip */
    public IEnumerator<Person> GetEnumerator() {
        return ((IEnumerable<Person>)_people).GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator() { return _people.GetEnumerator();}
}
Marc Gravell
Actually "never" is too harsh. Like never use a goto - yield is SOMETIMES a LITTLE slower, so in RARE EDGE CASES there is a case for not using yield. But again, this is REALLY rare, so no -1 here, just a note.
TomTom
@TomTom - revised to "very rarely" ;-p
Marc Gravell
+2  A: 

Types implementing IEnumerable require a method called GetEnumerator that returns an IEnumerator. In that example (which is pretty obsolete as of C# 2.0) there is an enumerator class PeopleEnum that implements IEnumerator. It's what's used internally by the C# foreach statement.

A more up to date example would look more like the following. Note there's no longer a need for a PeopleEnum class now that C# supports iterators. Effectively the compiler does all the heavy lifting for you.

public class People : IEnumerable
{
    private Person[] _people;
    public People(Person[] pArray)
    {
        _people = new Person[pArray.Length];

        for (int i = 0; i < pArray.Length; i++)
        {
            _people[i] = pArray[i];
        }
    }

   IEnumerator IEnumerable.GetEnumerator()
   {
       for (int i=0; i < _people.Length; i++) {
           yield return _people[i];
       }
   }
}
Josh Einstein
You could also just `return _people.GetEnumerator()` if it is untyped...
Marc Gravell
Yeah, I personally can't even remember the last time I implemented IEnumerable directly. I was just pointing out the fact that enumerator classes are largely extinct now.
Josh Einstein
I wrote one recently, but to support C# 1.2 ;-p
Marc Gravell