在 IEnumerable<dynamic> 上使用 LINQ 时出现编译器错误,但如果先将其转换为 IEnumerable<dynamic> 则不会

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

好的,所以我正在编写一些非常混乱的代码,因为我正在使用的库是 returning 动态类型层次结构。其中一些类型可以展开为动态类型列表,并使我能够在 LINQ 中使用这些动态对象层次结构,我写了一个小方法,基本上将一些动态对象转换为 IEnumerable

我有这个方法 return 是一个 IEnumerable 但是当我尝试将它与 LINQ 一起使用时我得到错误 "Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type.",但是如果我转换方法 return 从 IEnumerable 到 IEnumerable 的值(在我看来是一个空操作),它编译并且工作正常。

任何人都可以向我解释这种行为吗?

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.";
}

Foo(unknown)的结果是dynamic,不是IEnumerable<dynamic>。这是因为对 Foo 的调用是动态解析的,因为 unknowndynamic.

要静态解析Foo,您可以写object unknown = new ExpandoObject();,或者将调用更改为Foo((object) unknown)

更糟糕的是 dynamic 不支持扩展方法。即使 Tdynamic,也可以静态地找到 IEnumerable<T> 上的扩展方法,但是 C# 编译器不提供活动的 using 命名空间列表,所以如果你有纯 dynamic,运行时间不知道去哪个类搜索扩展方法。即使 .Select(value => value) 可以被编译,也许通过将它变成 Func<dynamic, dynamic> projection = value => value; 然后 unknown.Select(projection) (未经测试),它仍然会在 运行 时抛出异常。你需要明确地写 Enumerable.Select(unknown, projection) 才能让它工作。