.NET:推断通用类型的静态方法

问题描述:

假设我有.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

从你的错误消息:

类型参数的方法“[...].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

推理在推断类型委托的失败,不在名单:

// 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

以下也有效;我不知道为什么:

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

稍微挖一下,我发现你对另一个答案的怀疑是正确的。下面是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。

换言之,匿名的方法和方法基团(其广场),仅可以推断出的参数类型明确。我认为你提到的答案最后的理由很好地总结了它。因为类型推断不能总是从方法组中隐式地进行,所以编译器根据规范甚至不会尝试它。

这不起作用的原因是为了让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