.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?
从你的错误消息:
类型参数的方法“
[...].Map<T,T2>(System.Collections.Generic.List<T>, System.Func<T,T2>)
”不能从使用推断。尝试明确指定类型参数。
请注意,该错误消息说,它无法找出类型参数。也就是说,在解决类型参数T
或T2
中的一个时遇到问题。这是因为规范的第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);
是合法的。在这种情况下,inputs
是List<int>
这一事实,已经解决了T
是int
。
现在,更深层次的问题是为什么上述规范?也就是说,为什么方法组不能在类型参数解析中起作用?我认为这是因为这样的情况:
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?
}
}
只是为了多重验证,IanG从这个MSDN C#论坛似乎说同样的事情,并进入了一点为什么规范这么说:http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/a4847737-4a6b-4fcd-89f2-1b213aaf8422我发现它也有点启发。 – 2010-01-21 05:44:13
@jdk:嗯......看我的编辑。 – jason 2010-01-21 06:14:35
'Map
推理在推断类型委托的失败,不在名单:
// 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);
我不知道为什么,虽然 - 或许还有从Square
到Func<Int32, Int32>
签名只是没有隐含的类型转换?看起来很奇怪,Func<int, int> t = Square;
是有效的,但编译器不能自己跳跃...错误,也许呢?
阅读错误消息。方法''[[]]的类型参数,地图
我不明白这个答案是如何起作用的;这是错误的。这不是推断委托类型的失败。这是它无法使用方法组来推断其中一个类型参数。 – jason 2010-01-21 18:32:04
委托需要T2作为通用参数,并且编译器无法推断出T2的类型(根据规范,正如您所指出的那样),因此编译器无法推断出委托的类型。虽然它不是根本原因 - 你的解释显然是正确的 - 这在技术上并不正确。 – Dathan 2010-01-21 20:34:07
以下也有效;我不知道为什么:
var outputs = Map(inputs, i => Square(i));
因为与方法组不同,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
有趣。你介意发布编译器错误吗? – 2010-01-21 04:26:04
如果将Run()的第一行中的var更改为List,结果如何? –
2010-01-21 04:38:01
@tehMick:结果将是相同的。推断类型。 – 2010-01-21 04:42:14