Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does IEnumerable.GetEnumerator get called instead of IEnumerable<T>.GetEnumerator?

Looking at this code :

public class myWords : IEnumerable<string>
{
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries);

    public IEnumerator<string> GetEnumerator()
    {
        return f.Select(s => s + "2").GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return f.Select(s => s + "3").GetEnumerator();
    }
}

Running :

 myWords m = new myWords();
 foreach (var s in m)
 {
     Console.WriteLine(s);
 }

Yields

I 2
ve you2    // notice "2", so the generic Ienumerator has executed.

I understand that the non-generic IEnumerator version is for compatibility.

Question:

  1. In what scenario will the non-generic be invoked?
  2. How can I force my code to be run with the non-generic IEnumerator?
like image 488
Royi Namir Avatar asked Feb 16 '13 10:02

Royi Namir


People also ask

What is IEnumerable T?

IEnumerable<T> is the base interface for collections in the System. Collections. Generic namespace such as List<T>, Dictionary<TKey,TValue>, and Stack<T> and other generic collections such as ObservableCollection<T> and ConcurrentStack<T>.

What is IEnumerator IEnumerable GetEnumerator ()?

IEnumerable is an interface that allows us to iterate over a collection; it does so by exposing a single GetEnumerator() method, which in turn returns an IEnumerator: a simple interface that provides methods to access the current element in the collection, move to the next item, and reset back to the beginning of the ...

How does an IEnumerator differ from an IEnumerable?

An IEnumerator is a thing that can enumerate: it has the Current property and the MoveNext and Reset methods (which in . NET code you probably won't call explicitly, though you could). An IEnumerable is a thing that can be enumerated...which simply means that it has a GetEnumerator method that returns an IEnumerator .

What does GetEnumerator?

The C# GetEnumerator() method is used to convert string object into char enumerator. It returns instance of CharEnumerator. So, you can iterate string through loop.


3 Answers

The non-generic IEnumerator will be executed whenever code casts the class to the non-generic interface:

((IEnumerable)myWords).GetEnumerator(); // this calls the non-generic one

This is mostly relevant if you pass your class to some legacy function that requires a non-generic IEnumerator.

So if you have some library that contains a function and you pass your class to this function, it will use the non-generic IEnumerator

DoSomeStuffWithAnIEnumerable(IEnumerable x)
{
   var foo = x.GetEnumerator();

   // or, as stackx said, an example with foreach:
   foreach (var foo2 in x)
   Console.WriteLine(foo2);
}


DoSomeStuffWithAnIEnumerable(new myWords());


Note that it is perfectly valid to simply implement the non-generic IEnumerator using the generic one:

public class myWords : IEnumerable<string>
{
    ....    
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

That way you can be sure that they both have the same effects.

like image 139
HugoRune Avatar answered Oct 12 '22 23:10

HugoRune


The other answers sort of miss the point.

Interfaces do not matter at all if the (compile-time) type of what is beeing foreached has a public non-generic non-static method called GetEnumerator which takes zero arguments. (The return type of this method can be anything, generic or non-generic: interfaces will not matter.)

So the reason why the first of your methods is called, is that this is the public method.

You can change that:

public class myWords : IEnumerable<string>
{
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries);

    IEnumerator<string> IEnumerable<string>.GetEnumerator()
    {
        return f.Select(s => s + "2").GetEnumerator();
    }

    public IEnumerator GetEnumerator()
    {
        return f.Select(s => s + "3").GetEnumerator();
    }
}

To prove that interfaces are not needed, try this:

public class myWords // no interfaces!
{
    string[] f = "I love you".Split(new string[]{"lo"},StringSplitOptions.RemoveEmptyEntries);

    public IEnumerator GetEnumerator()
    {
        return f.Select(s => s + "3").GetEnumerator();
    }
}

However, it is wise to implement IEnumerable<>. Then your type can be used with Linq (extension methods on IEnumerable<>), and can be used as argument to other methods that simply require an IEnumerable<>.

Also, it is wise to have the method (or explicit interface implementation) that returns the non-generic IEnumerator just call through to the method (or explicit interface implementation) that returns IEnumerator<>. Having the two return distinct sequences is really confusing (but nice for asking and answering questions on how things work).

like image 37
Jeppe Stig Nielsen Avatar answered Oct 13 '22 00:10

Jeppe Stig Nielsen


The non-generic version of the IEnumerable is implemented with an explicit interface implementation. This means that you can only call the explicitly implemented function by casting to the interface.

The reason IEnumerable is implemented explicitly is that the method signatures are the same except for the return type.

Casting myWords explicitly to IEnumerable allows you to call the non-generic version like this: (IEnumerable)myWords.

The C# Guide explains how this works: Explicit interface implementation.

like image 42
Wouter de Kort Avatar answered Oct 12 '22 22:10

Wouter de Kort