Reputation: 23
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
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
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