LINQ:在动态列表上选择不编译

时间:2018-05-24 18:19:56

标签: c# linq generics dynamic

假设我有这样的方法:

static string ToString(dynamic d)
{
    return (string)d.ToString();
}

例如我有var tmp = new List<dynamic> { 1, "2", 345 };

为什么

IEnumerable<string> test = tmp.Select(ToString);

编译好,但

IEnumerable<string> test = tmp.Select(x => ToString(x));

没有按&#39;?吨

错误:

CS0266  Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<dynamic>'  to 'System.Collections.Generic.IEnumerable<string>'. An explicit conversion exists (are you missing a cast?)

VS 2015和VS 2017对所有&gt; = 4.5.0框架

都适用

2 个答案:

答案 0 :(得分:3)

您遇到了dynamic contagiondynamic可以是任何东西,因此任何方法或属性都可以返回任何内容。您传递给它的任何方法都可以是任何方法,因为当您传递dynamic作为参数时,会在运行时发生重载解析(如果您讨厌那些必须维护代码的人,则可以利用此方法)。

如果您将鼠标悬停在Select上方,它的类型为Select<dynamic, string>,则返回IEnumerable<String>。由于ToString(dynamic d)string中有return显式转换,并返回类型string,因此编译器可以确保ToString确实返回一个字符串。

IEnumerable<string> test = tmp.Select(ToString);

顺便说一下,如果我们对ToString()进行这些更改,上面的代码仍会编译,而lambda版本仍然不会:

    static string ToString(object d)
    {
        //  Remove the cast to string
        return d.ToString();
    }

当我们将tmp创建为List<dynamic>时,就会引入传染病。

在无法编译的lambda版本中,将鼠标悬停显示我们实际上正在调用Select<dynamic, dynamic>并返回IEnumerable<dynamic>

当您传递dynamic时,过载分辨率在运行时完成。编译器无法保证在编译时实际调用什么方法或返回什么方法。 Intellisense认为ToString是你的静态方法,但编译器并不相信它是真的。

IEnumerable<string> test2 = tmp.Select(x => ToString(x));

这个编译,因为我们有一个转换,其中重载决议不会影响它。

IEnumerable<string> test3 = tmp.Select(x => (string)ToString(x));
Jonathon Chase在评论中注意到我们通过将类型参数显式传递给Select来编译它:

IEnumerable<string> test4 = tmp.Select<dynamic, string>(x => ToString(x));

在我看来,重要的问题就是你的第一个案例编译的原因。我谨慎的猜测是因为你传递了对方法的引用而不是调用它,动态重载解析不会发生了。

将此视为占位符,直到有更深入理解的人对该问题感兴趣。

答案 1 :(得分:2)

我不会重复Ed关于“动态传染”的回答。但是会试着说明第一个案例成功编译的原因。

这里

IEnumerable<string> test = tmp.Select(ToString);

ToString是一个方法组 - 含糖功能,允许您使用某种快捷方式将方法作为具有适当签名的委托传递。可以在此帖子What is a method group in c#中找到一些其他信息。

这实际上是通过在封面下创建新的委托实例来实现的。因此,您的代码非常等同于

Func<dynamic, string> toString = new Func<dynamic, string>(ToString);
IEnumerable<string> test = tmp.Select(toString);

正如您所看到的,我们在这里有一个明确的委托返回类型,并且它不是动态的,因此我们不应该担心动态传染。