none
why ? I implements IEnumerable , put breakpoint the GetEnumerator, but never hit it. RRS feed

  • Question

  • Hi, everybody I have a question with implemeting IEnumerable and execute foreach see the breakpoint position. Why does VS debug not enter

    public class Person
    {
        public Person(string fName, string lName)
        {
            this.firstName = fName;
            this.lastName = lName;
        }
        public string firstName;
        public string lastName;
    }

    // Collection of Person objects. This class
    // implements IEnumerable so that it can be used
    // with ForEach syntax.
    public class People : IEnumerable
    {
        private Person[] _people;
        public People(Person[] pArray)
        {
            _people = new Person[pArray.Length];

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

        // Implementation for the GetEnumerator method.
        IEnumerator IEnumerable.GetEnumerator()
        {
            //Breakpoint here.
            System.Diagnostics.Debug.WriteLine( " GetEnumerator()");
            return (IEnumerator)GetEnumerator();
        }

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

    // When you implement IEnumerable, you must also implement IEnumerator.
    public class PeopleEnum : IEnumerator
    {
        private Person[] _people;

        // Enumerators are positioned before the first element
        // until the first MoveNext() call.
        int position = -1;

        public PeopleEnum(Person[] list)
        {
            _people = list;
        }

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

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

        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }

        public Person Current
        {
            get
            {
                    return _people[position];              

            }
        }
    }


    class Program
    {
       static void Main(string[] args)
        {
         Person[] persons = new Person[1]{ new Person("test ","Nguyen") };
         People people = new People(persons);
         foreach (var person in people)
            {
               Console.WriteLine( person.firstName + "  " + person.lastName);

            }
       }
    }

    ---------------------------------------------------------------------------

    expect: Break point is hit.

    actual: Break point not enter.

    Sunday, September 1, 2019 3:35 PM

Answers

  • The problem is that you have two GetEnumerator methods, and the code is using the other one. You can easily verify it by adding a second breakpoint in the other method (i.e., GetEnumerator() instead of IEnumerable.GetEnumerator()).

    If you look at the specification for the foreach instruction:

    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in

    you will see that foreach can work on classes that either implement IEnumerable or have a nethod called GetEnumerator that satisfies a series of conditions, which happen to be fulfilled in your code. Apparently, the compiler chose the second one given that the two alternatives were available in your program.

    Sunday, September 1, 2019 4:02 PM
    Moderator

All replies

  • The problem is that you have two GetEnumerator methods, and the code is using the other one. You can easily verify it by adding a second breakpoint in the other method (i.e., GetEnumerator() instead of IEnumerable.GetEnumerator()).

    If you look at the specification for the foreach instruction:

    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in

    you will see that foreach can work on classes that either implement IEnumerable or have a nethod called GetEnumerator that satisfies a series of conditions, which happen to be fulfilled in your code. Apparently, the compiler chose the second one given that the two alternatives were available in your program.

    Sunday, September 1, 2019 4:02 PM
    Moderator
  • Hi Antonio, 

    the trick here is about the two properties Current the implicitly and explicitly implemented ones, let see what's hapenning :

    public Person Current => _people[position]; 
    /* kind of explicit implementation, and this is where you should put the breakpoint */
    
    object IEnumerator.Current => Current;
    /* for the implicit implementation it should be accessed through a cast to the interface type which gives access to the methods and properties */ 
    

    when enumerating the compiler will call the public Current properties which in turn calls the IEnumerator.Current one.

    just put the breakpoint on the public "Current" getter.

    public class Person
    {
        public Person(string fName, string lName)
        {
            this.firstName = fName;
            this.lastName = lName;
        }
        public string firstName;
        public string lastName;
    }
    
    // Collection of Person objects. This class
    // implements IEnumerable so that it can be used
    // with ForEach syntax.
    public class People : IEnumerable
    {
        private Person[] _people;
        public People(Person[] pArray)
        {
            _people = new Person[pArray.Length];
    
            for (int i = 0; i < pArray.Length; i++)
            {
                _people[i] = pArray[i];
            }
        }
    
        // Implementation for the GetEnumerator method.
        IEnumerator IEnumerable.GetEnumerator()
        {
            //Breakpoint here.
            System.Diagnostics.Debug.WriteLine(" GetEnumerator()");
            return (IEnumerator)GetEnumerator();
        }
    
        public PeopleEnum GetEnumerator()
        {
            return new PeopleEnum(_people);
        }
    }
    
    // When you implement IEnumerable, you must also implement IEnumerator.
    public class PeopleEnum : IEnumerator
    {
        private Person[] _people;
    
        // Enumerators are positioned before the first element
        // until the first MoveNext() call.
        int position = -1;
    
        public PeopleEnum(Person[] list)
        {
            _people = list;
        }
    
        public bool MoveNext() => (++position < _people.Length);
    
    
        public void Reset()
        {
            position = -1;
        }
    
        public Person Current => _people[position];
                        
    
        object IEnumerator.Current => Current;
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person[] persons = new Person[2] { new Person("test1", "Nguyen1"), new Person("test2 ", "Nguyen2") };
            People people = new People(persons);
            foreach (var person in people)
            {
                Console.WriteLine(person.firstName + "  " + person.lastName);
    
            }
    
            Console.ReadLine();
        }
    }
    

    Best Regards,

    Mouad.

    Sunday, September 1, 2019 7:15 PM