Oskar Sjöberg
Oskar Sjöberg

Reputation: 2878

Compiler error when using LINQ on IEnumerable<dynamic> but not if you cast it to IEnumerable<dynamic> first

OK, so I am writing some really messy code as the library I am working with is returning dynamic type hierarchies. Some of these types can be unfolded to lists of dynamic types and to enable me to work with these dynamic object hierarchies in LINQ I wrote a little method which basically transforms some dynamic objects to an IEnumerable<dynamic>.

I have this method that returns an IEnumerable<dynamic> but when I try to use it with LINQ I get the error "Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type.", however if I cast the methods return value from IEnumerable<dynamic> to IEnumerable<dynamic> (a no-op in my mind), it compiles and works fine.

Can anybody explain this behavior to me?

void Main()
{
    Foo(null).Select(value => value); // OK... I was expecting this to work.

    dynamic unknown = new ExpandoObject();
    Foo(unknown).Select(value => value); //COMPILER ERROR: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type... this was a bit more unexpected.

    ((IEnumerable<dynamic>)Foo(unknown)).Select(value => value); // OK... this was really unexpected.
}

IEnumerable<dynamic> Foo(dynamic param)
{
    yield return "Tranformation logic from param to IEnumerable of param goes here.";
}

Upvotes: 4

Views: 882

Answers (1)

user743382
user743382

Reputation:

The result of Foo(unknown) is dynamic, not IEnumerable<dynamic>. That's because the call to Foo is resolved dynamically, as unknown is dynamic.

To resolve Foo statically, you can write object unknown = new ExpandoObject();, or change the call to Foo((object) unknown).

What makes this worse is that extension methods are not supported on dynamic. Extension methods on IEnumerable<T> can be statically found even if T is dynamic, but the C# compiler does not provide a list of active using namespaces, so if you have plain dynamic, the runtime doesn't know which classes to search for extension methods. Even if .Select(value => value) could be made to compile, perhaps by turning it into Func<dynamic, dynamic> projection = value => value; and then unknown.Select(projection) (untested), it would still throw an exception at run-time. You'd need to explicitly write Enumerable.Select(unknown, projection) to make it work.

Upvotes: 4

Related Questions