2011-05-04 66 views
12

考虑以下(毫无意义,但它是用于说明目的)测试类:动态,LINQ和选择()

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()); 
    } 
} 

为什么它不与下面的错误编译,在t.Select(x => ToStr(x))

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

第二种方法没有错误。

回答

10

我相信这里所发生的是,既然表达ToStr(x)涉及一个dynamic变量,整个表达式的结果类型也是dynamic;这就是为什么编译器认为它有一个IEnumerable<dynamic>它预计IEnumerable<string>

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

有两种方法可以解决这个问题。

使用显式转换:

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

这告诉表达式的结果肯定会是一个字符串的编译器,所以我们最终的IEnumerable<string>

与方法组替换拉姆达:

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

这种方式,编译方法组隐式表达转换为的λ。请注意,由于在表达式中没有提及dynamic变量x,所以其结果的类型可以立即推断为string,因为只有一种方法需要考虑,并且其返回类型为string

+2

+1用于解释,特别是方法组版本 – 2011-05-04 07:54:13

1

尝试这样的:

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

另一种可能性是明确指定的通用参数:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t) 
{ 
    return t.Select<dynamic, string>(x => ToStr(x)); 
} 
+1

它的工作,但为什么? – mathieu 2011-05-04 07:29:30

1

尝试return t.Select(x => ToStr(x)) as IEnumerable<string>

+2

它工作,但为什么? – mathieu 2011-05-04 07:30:57

1

它会出现C#编译器是确定拉姆达的类型在第一方法x => ToStr(x)作为Func<dynamic, dynamic>因此声明的IEnumerable返回IEnumerable<dynamic>类型。一个小小的变化x => (string)ToStr(x)似乎解决了它。

这很可能是因为类型推断规则 - 因为如果你改变了行这样的:

return t.Select<dynamic, string>(x => ToStr(x)); 

它编译没有错误。

有问题的特定类型的推理规则,不过,我不是太肯定的 - 但是如果你把这个代码:

public void foo(dynamic d) 
{ 
    var f = this.ToStr(d); 
    string s = f; 
} 

然后将鼠标悬停在编辑器中的“F”,你会看到intellisense将表达式的类型报告为“动态f”。这将是因为this.ToStr(d)是一个动态表达式,无论该方法本身及其返回类型在编译时是已知的。然后

编译器高兴地分配string s = f;,因为它能够进行静态分析是f很可能是类型,因为最终ToStr总是返回一个字符串。

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