2010-01-21 23 views
12

假設我有.NET:推斷通用類型的靜態方法

public static List<T2> Map<T,T2>(List<T> inputs, Func<T, T2> f) 
{ 
    return inputs.ConvertAll((x) => f(x)); 
} 

private int Square(int x) { return x*x; } 

public void Run() 
{ 
    var inputs = new List<Int32>(new int[]{2,4,8,16,32,64,128,256,512,1024,2048}); 

    // this does not compile 
    var outputs = Map(inputs, Square); 

    // this is fine 
    var outputs2 = Map<Int32,Int32>(inputs, Square); 

    // this is also fine (thanks, Jason) 
    var outputs2 = Map<Int32,Int32>(inputs, (x)=>x*x); 

    // also fine 
    var outputs2 = Map(inputs, (x)=>x*x); 
} 

爲什麼它不能編譯?

EDIT:錯誤是:

錯誤CS0411:用於方法的類型參數「Namespace.Map < T,T2 >(System.Collections.Generic.List <Ť>,System.Func < T,T2 >)'不能從使用中推斷出來。嘗試明確指定類型參數。

爲什麼我必須指定Map()函數的類型?難道它不能通過傳遞Func<T>推斷這一點嗎? (在我的情況,廣場)


答案是一樣的
C# 3.0 generic type inference - passing a delegate as a function parameter

+0

有趣。你介意發佈編譯器錯誤嗎? – 2010-01-21 04:26:04

+0

如果將Run()的第一行中的var更改爲List ,結果如何? – 2010-01-21 04:38:01

+0

@tehMick:結果將是相同的。推斷類型。 – 2010-01-21 04:42:14

回答

8

從你的錯誤消息:

類型參數的方法「[...].Map<T,T2>(System.Collections.Generic.List<T>, System.Func<T,T2>)」不能從使用推斷。嘗試明確指定類型參數。

請注意,該錯誤消息說,它無法找出類型參數。也就是說,在解決類型參數TT2中的一個時遇到問題。這是因爲規範的第25.6.4節(類型參數的推論)。這是規範中推斷泛型類型參數的部分。

沒有被從參數推斷出(但類型推斷成功)如果以下任意一項爲真:

[...]

的參數是方法組。

因此,編譯器無法使用代理類型Square來推斷T2的類型。請注意,如果你改變了聲明

public static List<T> Map<T>(List<T> inputs, Func<T, T> f) { 
     return inputs.ConvertAll((x) => f(x)); 
} 

然後

var outputs = Map(inputs, Square); 

是合法的。在這種情況下,inputsList<int>這一事實,已經解決了Tint

現在,更深層次的問題是爲什麼上述規範?也就是說,爲什麼方法組不能在類型參數解析中起作用?我認爲這是因爲這樣的情況:

class Program { 
    public static T M<T>(Func<T, T> f) { 
     return default(T); 
    } 

    public static int F(int i) { 
     return i; 
    } 

    public static float F(float f) { 
     return f; 
    } 

    static void Main(string[] args) { 
     M(F); // which F am I? 
    } 
} 
+0

只是爲了多重驗證,IanG從這個MSDN C#論壇似乎說同樣的事情,並進入了一點爲什麼規範這麼說:http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/a4847737-4a6b-4fcd-89f2-1b213aaf8422我發現它也有點啓發。 – 2010-01-21 05:44:13

+0

@jdk:嗯......看我的編輯。 – jason 2010-01-21 06:14:35

+0

'Map (List inputs,Func f)'?如果輸入和輸出是相同的類型,那麼地圖函數就不多了,呃? ;) – Juliet 2010-01-21 06:26:42

2

推理在推斷類型委託的失敗,不在名單:

// this is also fine 
var outputs3 = Map(inputs, new Func<int, int>(Square)); 

// more calls that compile correctly 
var outputs4 = Map(inputs, x => Square(x)); 

var outputs5 = Map(inputs, x => x * x); 

Func<int, int> t = Square; 
var outputs6 = Map(inputs, t); 

我不知道爲什麼,雖然 - 或許還有從SquareFunc<Int32, Int32>簽名只是沒有隱含的類型轉換?看起來很奇怪,Func<int, int> t = Square;是有效的,但編譯器不能自己跳躍...錯誤,也許呢?

+0

閱讀錯誤消息。方法''[[]]的類型參數,地圖(System.Collections.Generic.List ,System.Func )''。它無法弄清楚通用類型參數是什麼。它可以解析'T',因爲'inputs'是一個'List ',所以它知道'T'是'int'。因此它不能解決'T2'。看到我的答案。 – jason 2010-01-21 05:24:58

+0

我不明白這個答案是如何起作用的;這是錯誤的。這不是推斷委託類型的失敗。這是它無法使用方法組來推斷其中一個類型參數。 – jason 2010-01-21 18:32:04

+0

委託需要T2作爲通用參數,並且編譯器無法推斷出T2的類型(根據規範,正如您所指出的那樣),因此編譯器無法推斷出委託的類型。雖然它不是根本原因 - 你的解釋顯然是正確的 - 這在技術上並不正確。 – Dathan 2010-01-21 20:34:07

0

以下也有效;我不知道爲什麼:

var outputs = Map(inputs, i => Square(i)); 
+2

因爲與方法組不同,lambda表達式在類型參數推斷中起到了一定的作用。比照http://msdn.microsoft.com/en-us/library/ms364047(VS.80).aspx#cs3spec_topic4 – jason 2010-01-21 18:36:53

1

稍微挖一下,我發現你對另一個答案的懷疑是正確的。下面是C#3.0規範說的話:

7.4.2.1 - 對於每一個方法的參數EI:如果Ei爲匿名 函數或方法組,明確 參數類型推斷(7.4.2.7)是 ... 7.4.2.7 - ...如果E是具有 參數類型的明確類型的匿名函數U1 ... Uk和T是 具有參數類型的委託類型 V1 ... Vk然後對於每個Ui確切地推薦 ( §7.4.2.8)由Ui 製作,對應於Vi。

換言之,匿名的方法和方法基團(其廣場),僅可以推斷出的參數類型明確。我認爲你提到的答案最後的理由很好地總結了它。因爲類型推斷不能總是從方法組中隱式地進行,所以編譯器根據規範甚至不會嘗試它。

1

這不起作用的原因是爲了讓c#在一個方法上進行類型推斷,它必須知道轉換的另一端的委託類型。但是在這個時候,目標委託類型仍然不完全知道 - 只有T(int)是已知的,T2仍然沒有解決。

Func<int, int> f = Square; 
//works because we provided the destination type 
//of the conversion from Square to delegate 

Map(inputs, i => Square(i)); 
//works because the lambda follows the actual method call 
//and determines its own return type 
相關問題