2009-10-18 69 views
4

我有以下示例代碼(僅用於C#3.5學習目的!)。僅針對lambda表達式的隱式類型推斷?爲什麼?困惑!

我正在調用接受IEnumerable和排序函數的Sort函數。如果我使用lambda表達式(情況A)調用它,編譯器可以派生返回類型TResult,但是當我通過func SortInt(情況B)時,編譯器會引發錯誤!

我無法理解爲什麼編譯器不能在第二種情況下派生TResult!我似乎傳遞了完全相同的信息。還是那不準確?

請幫忙!

int[] intArray = { 1, 3, 2, 5, 1 }; 

IEnumerable<int> intArray2 = Sort(intArray, x => SortInt(x)); // <= CASE A - OK ! 

IEnumerable<int> nextIntArray = Sort(intArray, SortInt); // <= CASE B - Compile Error: Cannot Infer Type ! 

public static IEnumerable<TResult> Sort<T, TResult>(IEnumerable<T> toBeSorted,  
           Func<IEnumerable<T>, IEnumerable<TResult>> sortFunc) 
{ 
    return sortFunc(toBeSorted); 
} 

public static IEnumerable<int> SortInt(IEnumerable<int> array) 
{ 
    return array.OrderByDescending(x => x); 
} 
+0

你的名字就像是印度人 – 2009-11-09 10:28:27

回答

6

看起來好像在第二個示例中推理失敗,因爲編譯器無法對SortInt執行重載解析。

這可以是一個更透徹的解釋有用:

http://blogs.msdn.com/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

+0

那篇文章現在有些過時;基於這篇文章的反饋,我們確實最終改進了類型推斷算法,這種算法在這種情況下有所改進。 – 2009-10-18 15:41:54

+0

這就是說,這仍然是我們在這種情況下無法做重載解決方案的一個例子。 – 2009-10-18 15:52:40

+0

@羅伯特,謝謝你!那篇文章恰恰處理了這一點! – Preets 2009-10-19 10:58:36

5

我同意這很煩人,類型推斷不適用於方法組轉換。我希望C#編譯器在組中只有一個適用方法的常見情況下變得更聰明。但是,在複雜的情況下,最終可能會導致多種適用的重載,從而導致推斷出不同的類型。

選項:

  • 呼叫從一個lambda表達式的方法,因爲你已經證明
  • 指定類型參數明確,而不是依靠推理
  • 鑄造方法組涉及到的特定類型的(比第二個選項更糟)
  • 使用一個單獨的局部變量,並指定其類型明確

基本上我會堅持前兩個選項中的任何一個,因爲它們都是煩人的。請注意,第一個選項會有輕微的性能損失,因爲它是一個額外的間接級別,但通常不會很重要。

+0

謝謝你的解釋。我現在知道類型推斷不適用於方法組!理解'爲什麼'雖然不是我最簡單的任務! – Preets 2009-10-19 11:44:54

3
IEnumerable<int> intArray = Sort(intArray, x => SortInt(x)); // <= CASE A - OK ! 

在此聲明,在x => SortInt(x)是一個代表表示如下...

delegate(IEnumerable<int> x){ 
    return SortInt(x); 
} 

從技術上講,您不會在語句中的任何位置傳遞對SortInt的任何引用。

Func,IEnumerable>是一個委託類型,它意味着它需要一個「指向函數的指針」,但不是函數。 SortInt是一種方法。

如果你深入挖掘,你會發現x => SortInt(x)的實際編譯時間表示如下...

private static IEnumerable<int> __XYZ(IEnumerable<int> x){ 
    return SortInt(x); 
} 

private delegate IEnumerable<int> __XYZHANDLER(IEnumerable<int> x); 

,你的1號線將

IEnumerable<int> intArray = Sort(intArray, new __XYZHANDLER(__XYZ)); 

現在看你的第2行,SortInt是什麼,它的一個方法的名稱,它不是任何類型或實例。只有一個方法參數可以擁有某種類型的某個實例。新的委託是方法指針的一個實例,而不是任何方法。編寫器不會推斷SortInt爲x => SortInt(x)。

0

您的代碼有幾個問題妨礙了它的工作。

首先,你的類型並不都匹配。你正在傳遞一個int []作爲IEnumerable,但是你不能在不調用.AsEnumerable()的情況下這樣做。其次,T和TResult儘管在用法上相同(int),但與編譯器不同,但是您傳入一個int數組並期待IEnumerable,而不會說明結果的類型是。所以你必須傳遞TResult的類型(比如Sort(intArray.AsEnumerable(),SortInt)),它可以工作,但是你不需要TResult,因爲你只需要命令相同的類型即T。

所以,我擺脫TResult的和固定的類型:

void Main() 
{ 
    var intArray = new [] { 1, 3, 2, 5, 1 }; 
    var ints = Sort(intArray.AsEnumerable(), x => SortInt(x)); 
    var nextInts = Sort(intArray.AsEnumerable(), SortInt); 
} 

public static IEnumerable<T> Sort<T>(
       IEnumerable<T> toBeSorted, 
       Func<IEnumerable<T>, IEnumerable<T>> sortFunc) 
{   
    return sortFunc(toBeSorted); 
} 

public static IOrderedEnumerable<T> SortInt<T>(IEnumerable<T> array) 
{  
    return array.OrderByDescending(x => x); 
} 

我喜歡上面的,但是,這是最接近我能得到你的樣品,它的工作原理:

void Main() { 
    var intArray = new [] { 1, 3, 2, 5, 1 }; 
    var ints = Sort(intArray.AsEnumerable(), x => SortInt(x)); 
    var nextInts = Sort<int, int>(intArray.AsEnumerable(), SortInt); 
    } 

    public static IEnumerable<TResult> Sort<T, TResult>(
       IEnumerable<T> toBeSorted, 
       Func<IEnumerable<T>, IEnumerable<TResult>> sortFunc) 
    {  
     return sortFunc(toBeSorted); 
    } 
    public static IEnumerable<int> SortInt(IEnumerable<int> array){  
     return array.OrderByDescending(x => x); 
    } 

SortInt也可以是:

public static IOrderedEnumerable<T> SortInt<T>(IEnumerable<T> array){  
    return array.OrderByDescending(x => x); 
} 

Akash給出了使用lambda表達式時爲什麼會有差異的解釋。