Pregunta

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?
¿Fue útil?

Solución 2

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).

Otros consejos

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.

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top