foreach with classes that do not 'implement' IEnumerable / IEnumerator

2

I saw the comment of Equidad in one of my answers with an interesting detail and is that implemented an interface (or part of it) without using the operator : to the right of the name of the object.

Provided the following code:

public class TestEnumerator
{
    private int _veces;
    public TestEnumerator(int veces) { _veces = veces; }
    public object Current  { get { return "Hola " + _veces; } }
    public bool MoveNext() { return _veces-- > 0; }
}

public class Test
{
    public TestEnumerator GetEnumerator() { return new TestEnumerator(10); }
}

public static void Main()
{
    var test = new Test();

    foreach (var x in test)
        Console.WriteLine(x);
}

Indeed, the output is as expected:

Hola 9
Hola 8
Hola 7
Hola 6
Hola 5
Hola 4
Hola 3
Hola 2
Hola 1
Hola 0

What is equivalent to the implementation of a IEnumerable<T> e IEnumerator in their respective classes. What is this type of implementation called? Since it is a class with an enumerator, but it is not a collection, I would say that it is a half implementation of IEnumerable .

    
asked by NaCl 20.07.2016 в 16:28
source

2 answers

4

According to foreach, in (C #) :

  

An expression with a collection or arrangement. The element type of the collection must be convertible to the identifier type. Do not use an expression that evaluates to null .
  Evaluate a type that implements IEnumerable or a type that declares a GetEnumerator method. In the latter case, you must return either a type that implements IEnumerator or that declares all methods defined in IEnumerator .

The code you mentioned complies with the last case. The method Reset() does not < em> needs to be implemented necessarily .

In fact, a statement foreach of the form: 1

foreach (V v in x) embedded-statement

is then expanded to:

{
    E e = ((C)(x)).GetEnumerator();
    try {
       V v;
       while (e.MoveNext()) {
           v = (V)(T)e.Current;
           embedded-statement
       }
    }
    finally {
       ... // Dispose of e
    }
}

The variable e is not visible or accessible from the original code. The variable v is read-only in the embedded statement ( embedded-statement ).

Notes

  • See The C # Programming Language (Covering C # 4.0) by Anders Hejlsberg, Mads Torgersen, Scott Wiltamuth, Peter Golde, p. 519.
  • answered by 20.07.2016 / 18:29
    source
    2

    Here

    Iterators (C # and Visual Basic)

    is mentioned

      

    When creating an iterator for a class or struct, the complete IEnumerator interface does not have to be implemented. When the compiler detects the iterator, it automatically generates the Current, MoveNext, and Dispose methods of the IEnumerator or IEnumerator interface.

    I understand that the foreach is making use of Current , MoveNext() and GetEnumerator() but with this is enough, it does not validate that this interface IEnumerable and IEnumerator are declared, since you are equal expose the functionality that the iteration needs

    How to make a Visual C # class usable in a foreach statement

    The correct thing would be to define the interfaces that ensure that the class is iterable

    using System;
    using System.Collections;
    
    public class Program
    {
        public static void Main()
        {
            var test = new Test();
    
            foreach (var x in test)
                Console.WriteLine(x);
    
        }
    }
    
    public class TestEnumerator : IEnumerator 
    {
        private int _veces;
        public TestEnumerator(int veces) { _veces = veces; }
        public object Current  { get { return "Hola " + _veces; } }
        public bool MoveNext() { return _veces-- > 0; }
    
        public void Reset(){ _veces = 0; }
    }
    
    public class Test : IEnumerable
    {
        public IEnumerator GetEnumerator() { 
            return (IEnumerator)new TestEnumerator(10); 
        }
    }
    
        
    answered by 20.07.2016 в 17:57