C# 动态、linq和Select()

C# 动态、linq和Select(),c#,linq,dynamic,c#-4.0,C#,Linq,Dynamic,C# 4.0,考虑以下测试类(无意义,但仅用于说明): public class Test { public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t) { return t.Select(x => ToStr(x)); } public IEnumerable<string> ToEnumerableStrsWillC

考虑以下测试类(无意义,但仅用于说明):

public class Test
{
    public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
    {
        return t.Select(x => ToStr(x));
    }

    public IEnumerable<string> ToEnumerableStrsWillCompile(IEnumerable<dynamic> t)
    {
        var res = new List<string>();

        foreach (var d in t)
        {
            res.Add(ToStr(d));
        }

        return res;
    }

    public string ToStr(dynamic d)
    {
        return new string(d.GetType());
    }
}
第二种方法没有错误。

尝试以下方法:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(ToStr);
}
public IEnumerable到numerablestrswontcompile(IEnumerable t)
{
返回t.Select(ToStr);
}
另一种可能是明确指定泛型参数:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select<dynamic, string>(x => ToStr(x));
}
public IEnumerable到numerablestrswontcompile(IEnumerable t)
{
返回t.Select(x=>ToStr(x));
}

尝试
返回t.Select(x=>ToStr(x))作为IEnumerable
我相信这里发生的是,由于表达式
ToStr(x)
涉及一个
动态
变量,所以整个表达式的结果类型也是
动态的
;这就是为什么编译器认为它有一个
IEnumerable
,而它需要一个
IEnumerable

public IEnumerable到numerablestrswontcompile(IEnumerable t)
{
返回t.Select(x=>ToStr(x));
}
有两种方法可以解决此问题

使用显式强制转换:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(x => (string)ToStr(x));
}
public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(ToStr);
}
public IEnumerable到numerablestrswontcompile(IEnumerable t)
{
返回t.Select(x=>(string)ToStr(x));
}
这告诉编译器表达式的结果肯定是一个字符串,因此我们最终得到一个
IEnumerable

将lambda替换为方法组:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(x => (string)ToStr(x));
}
public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(ToStr);
}
public IEnumerable到numerablestrswontcompile(IEnumerable t)
{
返回t.Select(ToStr);
}
通过这种方式,编译器隐式地将方法组表达式转换为lambda。注意,由于表达式中没有提到<代码>动态< /COD>变量<代码> x>代码>,因此其结果的类型可以立即推断为字符串,因为只有一种方法可以考虑,它的返回类型是
string

看起来C编译器在第一个方法
x=>ToStr(x)
as
Func
中确定lambda的类型,因此声明作为
IEnumerable
返回的
IEnumerable
的类型。一个小小的改动
x=>(string)ToStr(x)
似乎可以解决这个问题

这很可能是因为类型推断规则-因为如果将行更改为:

return t.Select<dynamic, string>(x => ToStr(x));
然后将鼠标悬停在编辑器中的“f”上,您将看到intellisense将表达式的类型报告为“dynamic f”。这将是因为
This.ToStr(d)
是一个动态表达式,无论方法本身及其返回类型在编译时是否已知

然后编译器很乐意分配
字符串s=f
因为它能够静态分析
f
可能是的类型,因为最终
ToStr
总是返回一个字符串


这就是为什么第一个方法需要强制转换或显式类型参数的原因-因为编译器将
ToStr
设置为
动态类型;因为它至少有一个动态表达式作为它的一部分。

+1用于解释,特别是用于方法组版本
public void foo(dynamic d)
{
  var f = this.ToStr(d);
  string s = f;
}