2011-05-04 70 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類型;因爲它至少有一個動態表達式作爲它的一部分。