Implementation of GetEnumerator Method

mp3909

Well-known member
Joined
Apr 22, 2018
Messages
61
Location
UK
Programming Experience
3-5
Below is an implementation of the GetEnumerator method:

C#:
 IEnumerator IEnumerable.GetEnumerator()  
 {
     return (IEnumerator) GetEnumerator();
 }

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

Here is another way I have seen the GetEnumerator method being implemented:

C#:
public IEnumerator GetEnumerator()
{
    return new ListEnumerator();
}

private class ListEnumerator : IEnumerator
{
    
}


My question is, that can the first implementation be written as the second implementation?
 
Please see comment in this code.

C#:
using System;
using System.Collections;

class Person
{
    public string firstname { set; get; }
    public string lastname { set; get; }
}

class People : IEnumerable
{
    private Person[] _pple;

    public People(Person[] p)
    {
        _pple = new Person[p.Length];
        addPerson(p);
    }

    public void addPerson(Person[] p)
    {
        for (int i = 0; i < p.Length; i++)
        {
            _pple[i] = p[i];
        }
    }

    public IEnumerator GetEnumerator()
    {
        foreach(Person pp in _pple)
        {
            yield return pp;
        }
    }

}


class Program
{
    public static void Main()
    {
        Person[] p = new Person[4]
            {
            new Person() {firstname = "John", lastname = "A" },
            new Person() {firstname  = "Jim", lastname = "B"},
            new Person() {firstname = "Jack", lastname = "C"},
            new Person() {firstname = "James", lastname = "D"}
            };

        People pps = new People(p);

        foreach (Person a in pps)
        {
            Console.WriteLine(a.firstname);
        }

        var x = pps.GetEnumerator();
        while(x.MoveNext())
        {
            Console.WriteLine(x.Current);   //I am trying to replicate the above foreach loop but I cannot seem to print out the firstname here
        }
    }
}
 
Current is type object, so you cast it as Person: ((Person)x.Current).firstname
 
In this day and age, you should be implementing generic versions of interfaces if they exist. If you implement a generic interface for which a non-generic version exists as well, you should implement both and then defer to the generic implementation in the non-generic implementation. In the case of your People class, you should be doing this:
C#:
class People : IEnumerable, IEnumerable<Person>
{
    private Person[] _pple;

    public People(Person[] p)
    {
        _pple = new Person[p.Length];
        addPerson(p);
    }

    public void addPerson(Person[] p)
    {
        for (int i = 0; i < p.Length; i++)
        {
            _pple[i] = p[i];
        }
    }

    public IEnumerator<Person> GetEnumerator()
    {
        foreach (var pp in _pple)
        {
            yield return pp;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

}
The implementation of IEnumerable.GetEnumerator is made explicit, so it is only accessible if a People instance is explicitly cast as type IEnumerable. That means that your current code would be calling the implementation of IEnumerator<Person>.GetEnumerator, in which case the Current property will be type Person rather than type object, so there's no cast necessary to access members of the Person type:
C#:
var x = pps.GetEnumerator();
while (x.MoveNext())
{
    Console.WriteLine(x.Current.firstname);
}
By the way, if you call GetEnumerator directly, rather than using a foreach loop, then you should dispose the object it returns when you're done with it. That means that you should create it with a using statement:
C#:
using (var x = pps.GetEnumerator())
{
    while (x.MoveNext())
    {
        Console.WriteLine(x.Current.firstname);
    }
}
 
Is there a reason why the "public" keyword is not allowed in the below:

C#:
    public IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
 
Is there a reason why the "public" keyword is not allowed in the below:
Because it is an explicit implementation. Explicitly implemented interface members are never accessible via a reference of the class type and always accessible via a reference of the interface type, so an access modifier is not useful or meaningful and is thus not allowed.
 
is that why you need to cast the object as the interface type in order to access an explicit interface member?

C#:
var c1 = new Class1();
var ce1 = (IEnumerable) c1;
var e = ce1.GetEnumerator();
 
In this piece of code:

C#:
 public IEnumerator<Person> GetEnumerator()
    {
        foreach (var pp in _pple)
        {
            yield return pp;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

Hi, you mentioned if my people object is not casted as an IEnumerable, then it will call the IEnumerator<Person> GetEnumerator() instead, right?
What if it was casted as an IEnuermator, then it will call the explicit implementation and the other implementation (i.e. non explicit version )will be redundant, right?
Or do both implementations work together or are they totally independent.
 
is that why you need to cast the object as the interface type in order to access an explicit interface member?
You need to cast an object as an interface type to access explicit implementations of members of that interface because that's how explicit implementations work. An explicit implementation is one that can only be accessed via a reference of the interface type. If it could be accessed otherwise then it wouldn't be an explicit implementation.
 
In this piece of code:

C#:
 public IEnumerator<Person> GetEnumerator()
    {
        foreach (var pp in _pple)
        {
            yield return pp;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

Hi, you mentioned if my people object is not casted as an IEnumerable, then it will call the IEnumerator<Person> GetEnumerator() instead, right?
What if it was casted as an IEnuermator, then it will call the explicit implementation and the other implementation (i.e. non explicit version )will be redundant, right?
Or do both implementations work together or are they totally independent.
In that example, the explicit implementation calls the other overload so the same functionality is used regardless of which gets called. That is generally the preferable option and obeys the DRY principle. If you need to change the implementation then you only have to do so in one place. If there is a good reason to provide two separate implementations then you can do so.

It is generally preferable to implement the IEnumerable<T> interface as well as IEnumerable because LINQ is based on IEnumerable<T>. You don't have to if you don't want to and, on occasion, there may be good reason not to but you don't lose anything by implementing both so why not do so? Even using a foreach loop, a generic implementation is preferable because it means that you don't have to specify the type of the loop control variable as it can be inferred. A foreach loop over an IEnumerable implementation will use type object for the loop control variable by default, whereas a foreach loop over an IEnumerable<T> implementation will use type T by default.
 
Back
Top Bottom