I can’t use var in my foreach loop

A month ago, I asked a coworker to implement IEnumerable<blah> on a blahCollection type that we had implemented back in 1.1. We had only recently moved to the C# 3.0 compiler for this project and I was a little surprised that I wasn’t able to use the var keyword inside a foreach statement of this type. (I could use it, but it types the object as object instead of a strong type, which is effectively “cannot use it”.)

Its a combination of “we used to implement our own iterators” and the way the foreach finds the GetEnumerator method. foreach doesn’t actually work off of IEnumerable or IEnumerable<T>. It just looks for a GetEnumerator method. So when you implement IEnumerable<T>, foreach won’t find it if it is an explicit implementation. It needs to be an implicit implementation. But this will conflict with the old IEnumerable implementation, and so the change is a break in binary compatibility. Its a small price to pay for use of new language features.

note: Bill Wagner told us all of the above but wrapped it in a <guess> tag when he emailed us.

I wrote a test to show the behavior. Huge thanks to someone (I don’t recall who) in #csharp on freenode who helped me in creating the CompileTimeType method. Its very different to me to use the compiler in this way.

using System;
using System.Collections.Generic;
using System.Collections;
using NUnit.Framework;
namespace Scratch.IEnumerableFixtures
{
    [TestFixture]
    public class foreachFixture
    {
        [Test]
        public void implicitIsStrong()
        {
            foreach(var item in new I())
                Assert.AreEqual(typeof(int), CompileTimeType(item));
        }
        [Test]
        public void explicitIsWeak()
        {
            foreach (var item in new E())
                Assert.AreEqual(typeof(object), CompileTimeType(item));
        }
        [Test]
        public void noInterface()
        {
            foreach (var item in new N())
                Assert.AreEqual(typeof(object), CompileTimeType(item));
        }
        [Test]
        public void noInterfaceGeneric()
        {
            foreach (var item in new NG())
                Assert.AreEqual(typeof(int), CompileTimeType(item));
        }
        public Type CompileTimeType<T>(T item) { return typeof(T); }
        class I : IEnumerable<int>, IEnumerable
        {
            public IEnumerator<int> GetEnumerator() { yield return 1; }
            IEnumerator IEnumerable.GetEnumerator() { yield return 1; }
        }
        class E : IEnumerable<int>, IEnumerable
        {
            IEnumerator<int> IEnumerable<int>.GetEnumerator() { yield return
1; }
            public IEnumerator GetEnumerator() { yield return 1; }
        }
        class N { public IEnumerator GetEnumerator() { yield return 1; } }
        class NG { public IEnumerator<int> GetEnumerator() { yield return
1; } }
    }
}