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?
 
The first example is an explicit implementation of the interface while the second is not. An explicit implementation is one that can only be accessed by casting an object of the type as the interface type. For example, if I just add a class to a project, specify that it implements the IEnumerable interface and then tell the IDE to add missing members, this is what I get:
C#:
class Class1 : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }
}
Based on that implementation, I can do this:
C#:
var c1 = new Class1();
var e = c1.GetEnumerator();
If I click on the GetEnumerator method and select the To explicit implementation option, the code is changed to this:
C#:
class Class1 : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}
The previous usage of the c1 class and its GetEnumerator method will no longer compile. The compiler will tell you:
'Class1' does not contain a definition for 'GetEnumerator' and no accessible extension method 'GetEnumerator' accepting a first argument of type 'Class1' could be found (are you missing a using directive or an assembly reference?)
In order to access an explicit implementation of an interface member, you must cast the object as the interface type, e.g.
C#:
var c1 = new Class1();
var ce1 = (IEnumerable) c1;
var e = ce1.GetEnumerator();
In the case of the IEnumerable and IEnumerable<T> interfaces, whether the implementation is explicit or not rarely makes a difference. That's because such objects are generally enumerated using a foreach loop and, internally, it will cast the object being enumerated as one of those interfaces.
C#:
var c1 = new Class1();

foreach (var item in c1)
{
    // ...
}
The other difference between those examples appears to be that the first one uses a type implementing IEnumerator that is declared outside the type it enumerates while the second example uses a type declared inside the type it enumerates. Either is valid but a nested type can only be used to enumerate instances of the type it is declared in, while an independent type could, in theory, be used to enumerate multiple other types.
 
Thank you jmcilhinney, very well explained.

jmcilhinney, if you was creating your own custom type and wanted to ensure you can use the foreach loop on your own custom type, which way would you use to implement the IEnumerable Interface in your custom class?
 
The only reason I have found to create an explicit implementation is if there's a clash between members of multiple interfaces. There may be other reasons that I'm not aware of but I would generally not make the implementation explicit. That said, given that IEnumerable and its generic counterpart are primarily to facilitate foreach loops, making implementations of those can make the class appear cleaner without sacrificing functionality.
 
C#:
using System;
using System.Collections;


class Person
{
    private string fistname;
    private string lastname;

    public string Fname
    {
        set
        {
            this.fistname = value;
        }

        get
        {
            return this.lastname;
        }
    }
}


class People : IEnumerable
{
    private Person[] _people;  //create an array called people that hold object Person

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

    public IEnumerator GetEnumerator()
    {
        return new PeopleEnumerator(_people);
    }


    private class PeopleEnumerator : IEnumerator
    {
        private int _currentIndex = -1;

        public PeopleEnumerator(Person[] list)
        {
            _people = list;   //here it does not recognise _people, but why?
        }

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

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

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

        }
}


I am getting an error:

C#:
Severity    Code    Description    Project    File    Line    Suppression State
Error    CS0120    An object reference is required for the non-static field, method, or property 'People._people'    ConsoleApp45    c:\users\mp88_\source\repos\ConsoleApp45\ConsoleApp45\Program.cs    49    Active

I know why I am getting this error: The private nested class
C#:
PeopleEnumerator
is trying to access
C#:
_people
which is declared in the outer class
C#:
People
as a private member.
But what I don't understand is that I thought private members are accessible by a nester inner class, no?
 
The issue is not that you cannot access the _people field because it is private. The issue is that you are not accessing it via an instance of the People class, as the error message says. The fact that the PeopleEnumerator class is declared inside the People class does not mean that each PeopleEnumerator instance has implicit access to a People instance. Your People class is creating an instance of the PeopleEnumerator class so that PeopleEnumerator object would have to know about the People object that created it specifically.
 
Ahh, I see.
So just to clarify, a nested class can only access private members of the outer class via an instance of the outer class?
Thank You
 
Code in a nested class is just like code in any other class. It can access anything that is in scope and it requires a reference to an instance of a type to invoke instance members of that type. Private fields of an outer type are in scope for any code within that type, so code in a nested class can access them. That doesn't change the fact that an instance is required to access an instance member though. Only Shared members can be access without an instance.
 
When you create a class that implements the IEnumerable interface, then is it mandatory that you must also implement IEnumerator?
The reason why I ask is that I have seen some codes that implements the IEnumerator interface like the code below:

C#:
using System;
using System.Collections;


class Person
{
    private string fistname;
    private string lastname;

    public string Fname
    {
        set
        {
            this.fistname = value;
        }

        get
        {
            return this.fistname;
        }
    }
}


class People : IEnumerable
{
    private Person[] _people;  //create an array called people that hold object Person

    public People(Person[] pArray)
    {
        _people = new Person[pArray.Length];

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

    public IEnumerator GetEnumerator()
    {
        return new PeopleEnumerator(_people);
    }


    private class PeopleEnumerator : IEnumerator
    {
        private Person[] _plist;

        private int _currentIndex = -1;

        public PeopleEnumerator(Person[] list)
        {
            _plist = list;
        }

        public bool MoveNext()
        {
            _currentIndex++;
            return (_currentIndex < _plist.Length);
        }

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

        public object Current
        {
            get
            {
                try
                {
                    return _plist[_currentIndex];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }

        }
    }
}

but then on the other hand I have seen codes that completely ignore implementing the IEnumerator interface like this one:

C#:
class MyArrayList : IEnumerable
{
    object[] m_Items = null;
    int freeIndex = 0;

    public MyArrayList()
    {
        // For the sake of simplicity lets keep them as arrays
        // ideally it should be link list
        m_Items = new object[100];
    }

    public void Add(object item)
    {
        // Let us only worry about adding the item
        m_Items[freeIndex] = item;
        freeIndex++;
    }

    // IEnumerable Member
    public IEnumerator GetEnumerator()
    {
        foreach (object o in m_Items)
        {
            // Lets check for end of list (its bad code since we used arrays)
            if(o == null)
            {
                break;
            }

            // Return the current element and then on next function call
            // resume from next element rather than starting all over again;
            yield return o;
        }
    }
}


Both pieces of code are trying to achieve one goal - that is to be able to use the "foreach" statement on their custom type.
So I don't understand why would one go and implement the IEnumerator interface when you can still achieve the same goal without its implementation..
 
The only requirement when you implement the IEnumerable interface is that you implement a method named GetEnumerator and it returns an IEnumerator instance. That's it. Obviously if you are to return an IEnumerator though, that needs to be implemented somewhere. Before the existence of iterators, i.e. methods that use yield to return multiple objects one at a time, you had no choice but to return an object that implemented IEnumerator directly, which often meant doing it yourself. The addition of iterators has made things easier but obviously old code will still do it the old way and some people who aren't comfortable with iterators may still do it the old way even now.
 
hmm, I am still a little confused getting my head around this.
You definitely need to implement the IEnumerable interface - understood.
The code below is implementing the IEnumerable interface but its not returning an IEnumerator, correct?

C#:
public IEnumerator GetEnumerator()
    {
        foreach (object o in m_Items)
        {
            // Lets check for end of list (its bad code since we used arrays)
            if(o == null)
            {
                break;
            }

            // Return the current element and then on next function call
            // resume from next element rather than starting all over again;
            yield return o;
        }
    }
 
It is returning an IEnumerator. It must be, because that's the return type of the method. What it is not doing is explicitly returning an object of a type that you declared that implements IEnumerator. When you use yield return, you are making use of language features that will generate code to return an IEnumerator object create by the system. Try calling that method and assigning the result to a variable and then examine it in the debugger to see exactly what type it is.
 
510


I tried calling the method IEnumerator.GetEnumerator() (i.e. the one and only method in the IEnumerable Interface), assigned it to variable t and as you can see from the image, t is of the type "System.Collections.IEnumerator" (aka "IEnumerator").

jmcilhinney is that what you was trying to get at?

Thank You
 
Yes, that is exactly what I was saying. The method's return type is IEnumerator so the object it returns must be that type. The actual type name indicates that it is an anonymous type, which is something generated on the fly, as required by the situation. When you use yield return and return an object of type T, the system generates a type that is capable of enumerating a list of objects of type T.
 
Back
Top Bottom