JnManso
JnManso

Reputation: 23

Yield return and dynamic

Is possible to invoke a "IEnumerable/yield return" method when using dynamic?

I'm asking this because I'm getting the error below when I call the "Test(States1.GetNames())" method.

Error: "Additional information: 'object' does not contain a definition for 'GetEnumerator'"

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);
            }
        }
    }
}

Thanks

Upvotes: 1

Views: 822

Answers (2)

Tim S.
Tim S.

Reputation: 56536

It fails because the IEnumerable<string> class generated by your yield code explicitly implements its interfaces (including the GetEnumerator you're trying to use). You can call the method like this:

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

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

Or, since you don't need dynamic for any reason I can see here, maybe just:

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

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

Or

public static void Iterate<T>(IEnumerable<T> itemList)

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1499760

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.)

You can iterate with foreach though:

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

This has the additional benefit that it will dispose of the iterator for you, which your previous code didn't do :)

Alternatively, you add overloads for Iterate to take IEnumerable or IEnumerable<T>, and let execution-time overload resolution within Test do the right thing (due to state being dynamic too).

Upvotes: 5

Related Questions