C# `foreach` behaviour — Clarification?

I've read Eric's article here about foreach enumeration and about the different scenarios where foreach can work

In order to prevent the old C# version to do boxing , the C# team enabled duck typing for foreach to run on a non- Ienumerable collection.(A public GetEnumerator that return something that has public MoveNext and Current property is sufficient(.

So , Eric wrote a sample :

class MyIntegers : IEnumerable
{
  public class MyEnumerator : IEnumerator
  {
    private int index = 0;
    object IEnumerator.Current { return this.Current; }
    int Current { return index * index; }
    public bool MoveNext() 
    { 
      if (index > 10) return false;
      ++index;
      return true;
    }
  }
  public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
  IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}

But I believe it has some typos (missing get accessor at Current property implementation) which prevent it from compiling (I've already Emailed him).

Anyway here is a working version :

class MyIntegers : IEnumerable
{
  public class MyEnumerator : IEnumerator
  {
    private int index = 0;
      public void Reset()
      {
          throw new NotImplementedException();
      }

      object IEnumerator.Current {
          get { return this.Current; }
      }
    int Current {
        get { return index*index; }
    }
    public bool MoveNext() 
    { 
      if (index > 10) return false;
      ++index;
      return true;
    }
  }
  public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
  IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}

Ok.

According to MSDN :

A type C is said to be a collection type if it implements the System.Collections.IEnumerable interface or implements the collection pattern by meeting all of the following criteria:

  • C contains a public instance method with the signature GetEnumerator() that returns a struct-type, class-type, or interface-type, which is called E in the following text.

  • E contains a public instance method with the signature MoveNext() and the return type bool.

  • E contains a public instance property named Current that permits reading the current value. The type of this property is said to be the element type of the collection type.

OK. Let's match the docs to Eric's sample

Eric's sample is said to be a collection type because it does implements the System.Collections.IEnumerable interface ( explicitly though). But it is not(!) a collection pattern because of bullet 3 : MyEnumerator does not public instance property named Current.

MSDN says :

If the collection expression is of a type that implements the collection pattern (as defined above), the expansion of the foreach statement is:

E enumerator = (collection).GetEnumerator();
try {
   while (enumerator.MoveNext()) {
      ElementType element = (ElementType)enumerator.Current;
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}

Otherwise , The collection expression is of a type that implements System.IEnumerable (!), and the expansion of the foreach statement is:

IEnumerator enumerator = 
        ((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
   while (enumerator.MoveNext()) {
      ElementType element = (ElementType)enumerator.Current;
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}

Question #1

It seems that Eric's sample neither implements the collection pattern nor System.IEnumerable - so it's not supposed to match any of the condition specified above. So how come I can still iterate it via :

 foreach (var element in (new MyIntegers() as IEnumerable ))
             {
                 Console.WriteLine(element);
             }

Question #2

Why do I have to mention new MyIntegers() as IEnumerable ? it's already Ienumerable (!!) and even after that , Isn't the compiler is already doing the job by itself via casting :

((System.Collections.IEnumerable)(collection)).GetEnumerator() ?

It is right here :

 IEnumerator enumerator = 
            ((System.Collections.IEnumerable)(collection)).GetEnumerator();
    try {
       while (enumerator.MoveNext()) {
       ...

So why it still wants me to mention as Ienumerable ?

Jon Skeet
people
quotationmark

MyEnumerator does not has the required public methods

Yes it does - or rather, it would if Current were public. All that's required is that it has:

  • A public, readable Current property
  • A public MoveNext() method with no type arguments returning bool

The lack of public here was just another typo, basically. As it is, the example doesn't do what it's meant to (prevent boxing). It's using the IEnumerable implementation because you're using new MyIntegers() as IEnumerable - so the expression type is IEnumerable, and it just uses the interface throughout.

You claim that it doesn't implement IEnumerable, (which is System.Collections.IEnumerable, btw) but it does, using explicit interface implementation.

It's easiest to test this sort of thing without implementing IEnumerable at all:

using System;

class BizarreCollection
{
    public Enumerator GetEnumerator()
    {
        return new Enumerator();
    }

    public class Enumerator
    {
        private int index = 0;

        public bool MoveNext()
        {
            if (index == 10)
            {
                return false;
            }
            index++;
            return true;
        }

        public int Current { get { return index; } }
    }
}

class Test
{
    static void Main(string[] args)
    {
        foreach (var item in new BizarreCollection())
        {
            Console.WriteLine(item);
        }
    }
}

Now if you make Current private, it won't compile.

people

See more on this question at Stackoverflow