2011-10-12 43 views
11

考慮方法推理不與方法組工作

void Main() 
{ 
    var list = new[] {"1", "2", "3"}; 
    list.Sum(GetValue); //error CS0121 
    list.Sum(s => GetValue(s)); //works ! 
} 

double GetValue(string s) 
{ 
    double val; 
    double.TryParse(s, out val); 
    return val; 
} 

爲CS0121錯誤的描述是

呼叫是下列方法或屬性之間曖昧: 'System.Linq.Enumerable.Sum<string>(System.Collections.Generic.IEnumerable<string>, System.Func<string,decimal>)''System.Linq.Enumerable.Sum<string>(System.Collections.Generic.IEnumerable<string>, System.Func<string,decimal?>)」

我不明白的是,什麼在形成不s => GetValue(s)給編譯器,根本GetValue不 - 不是前者,後者的語法糖?

回答

18

馬克的答案是正確的,但可以使用更多一點的解釋。

問題的確是由於處理方法組的方式和如何處理lambda表達式之間的細微區別。

具體地,細微區別在於方法組被認爲是轉換爲委託類型單獨的基礎上是否參數匹配,而不是還是否返回類型匹配的基礎上。 Lambdas檢查參數和返回類型。

這個奇怪規則的原因是方法組轉換爲委託本質上是重載決議問題的解決方案。假設d是委託類型double D(string s)而M是包含採用字符串並返回一個字符串的方法的方法組。當解決從M到D的轉換的含義時,我們會重載解析,就像你說M(字符串)一樣。重載決策會選擇接受一個字符串,並返回一個字符串的男,所以M爲轉換爲該委託類型即使轉換將導致錯誤後。正如「正常」重載解析會成功,如果你說「string s = M(null);」 - 重載解析成功,即使稍後導致轉換失敗。

這條規則很微妙,有點奇怪。其結果是您的方法組可以轉換爲所有不同的委託類型,這是的第二個參數,每個版本的總和需要代表。由於沒有找到最佳轉換,所以方法組Sum上的超載分辨率不明確。

方法組轉換規則似乎合理,但在C#中有點奇怪。我有些惱火,他們不符合更直觀正確的lambda轉換。

+0

如果你有時間機器,你會如何改變它們?你會讓他們考慮返回類型,比如lambdas?或者你會做出不同的微妙變化? – configurator

+0

有趣!我在LinqPad中玩過它,事實上,即使我有'double D1(string s)'和'int D1(string s)',具有'string M(string)'簽名的方法組也會產生一個模糊的調用錯誤 - 即使兩個簽名都不適合。這與lambda相反,它會嘗試所有這些,並在它嘗試的最後一個(第一個?)給它一個轉換錯誤。 –

+0

但是,如果沒有重載解析問題(並且使用了錯誤的簽名),則方法組錯誤(「M具有錯誤的返回類型」)比lambda(「不能隱式轉換類型」字符串「 '雙'」) –

8

s => GetValue(s)是一個lambda表達式,而GetValue是一個方法組,它是一個完全不同的東西。它們都可以被認爲是語法糖new Func<string,double>(...)但它們的相互關係的唯一途徑就是lambda表達式包括對GetValue()通話。談到轉換到委託時,方法組與lambda表達式在返回類型方面有不同的轉換規則。見Why is Func<T> ambiguous with Func<IEnumerable<T>>?Overloaded method-group argument confuses overload resolution?

+0

有趣的(upvoted) - 將檢查鏈接 –