Yield return and dynamic gotchas with explicit interface implementation

This week while I’m doing a little demo to measure the time using a yield return method vs return a static list. I decided to use the keyword dynamic to simplify my code and avoid  the methods overload. Quickly i found when I called the yield return method it simple crashed with a kinda gotchas between dynamic and IEnumerable<T>. It fails when yield code explicitly implements its interfaces. The solution is to help the execution-time overload resolution to find the most specific method. 

Code demo:

using System;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics;

namespace YieldDemo
{
    public class States1
    {
          public static IEnumerable<string> GetNames()
        {
            yield return "Alabama";
            yield return "Alaska";
            yield return "Arizona";
            yield return "Arkansas";
            yield return "California";
            yield return "Others ...";
        }
    }

    public class States2
    {
        private static readonly IList<string> _names;

        static States2()
        {
            _names = new List<string>() {"Alabama", 
                                     "Alaska",
                                     "Arizona",
                                     "Arkansas",
                                     "California",
                                     "Others ..." };

        }

        public static IList<string> GetNames()
        {
            return _names;
        }
    }

    public class Program
    {

        static void Main()
        {
            Test(States2.GetNames());
            Test(States1.GetNames());

            Console.ReadLine();
        }

        public static void Test(dynamic state)
        {
            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();
            Iterate(state);            
            stopwatch.Stop();

            Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
        }

        public static void Iterate(dynamic itemList)
        {
            var enumerator = itemList.GetEnumerator();

            while (enumerator.MoveNext())
            {
                Console.WriteLine(enumerator.Current);
            }
        }
    }
}

When i call the following method:

Test(States1.GetNames())

I received the error “Additional information: ‘object’ does not contain a definition for ‘GetEnumerator'”.

Without any clue why this is happening. I posted a question in StackOverflow and I got some help.

“The problem is that the iterator block implementation uses explicit interface implementation to implement IEnumerable<T>… and explicit interface implementation doesn’t play nicely with dynamic typing in general. (You don’t need to use iterator blocks to see that. See my article on Gotchas in Dynamic Typing for more details.)”

“It fails because the IEnumerable<string> class generated by your yield code explicitly implements its interfaces (including the GetEnumerator you’re trying to use).”

So …

One way of avoiding the explicit interface implementation issue is to use dynamic typing to perform execution-time overload resolution to find the most specific method … the compiler won’t always know which overload to choose.

Solution 1:

Using the foreach to iterate.

public static void Iterate(dynamic itemList)
{
    foreach (dynamic item in itemList)
    {
        Console.WriteLine(item);
    }
}

Solution 2:

public static void Iterate(dynamic itemList)
{
    var enumerator = ((IEnumerable)itemList).GetEnumerator();

    while (enumerator.MoveNext())
    {
        Console.WriteLine(enumerator.Current);
    }
}

Solution 3:

public static void Iterate(IEnumerable itemList)
{
    var enumerator = itemList.GetEnumerator();

    while (enumerator.MoveNext())
    {
        Console.WriteLine(enumerator.Current);
    }
}

Solution 4:

public static void Iterate<T>(IEnumerable<T> itemList)
{
    var enumerator = itemList.GetEnumerator();

    while (enumerator.MoveNext())
    {
        Console.WriteLine(enumerator.Current);
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *