假设我有
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);
}
为什么不编译?
编辑:错误是:
错误CS0411:无法从使用中推断出方法'Namespace.Map&lt; T,T2&gt;(System.Collections.Generic.List&lt; T&gt;,System.Func&lt; T,T2&gt;)'的类型参数。尝试明确指定类型参数。
为什么我必须指定Map()函数的类型?它能否从传递的Func<T>
中推断出来? (在我的例子中,Square)
答案与中文相同 C# 3.0 generic type inference - passing a delegate as a function parameter?
答案 0 :(得分:10)
从错误消息中:
无法从用法中推断出方法“
[...].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);
是合法的。在这种情况下,它已经解决了T
int
inputs
List<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?
}
}
的问题。
现在,更深层次的问题是为何上述规范?也就是说,为什么方法组不能在类型参数解析中发挥作用?我认为这是因为这样的情况:
{{1}}
答案 1 :(得分: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);
我不知道为什么 - 也许从Square
到Func<Int32, Int32>
的签名中没有隐含的类型转换? Func<int, int> t = Square;
是有效的似乎很奇怪,但编译器无法自行实现飞跃...... Bug,也许?
答案 2 :(得分:1)
经过一番挖掘,我发现你对其他答案的怀疑是正确的。以下是C#3.0规范所说的内容:
7.4.2.1 - 对于每个方法参数Ei:如果Ei是匿名的 功能或方法组,显式 参数类型推断(7.4.2.7)是 制作... 7.4.2.7 - ...如果E是一个显式类型的匿名函数 参数类型U1 ... Uk和T是a 委托类型与参数类型 V1 ... Vk然后为每个Ui精确 推理(第7.4.2.8节)由Ui制作 对应的Vi。
换句话说,匿名方法和方法组(Square是),只能明确地推断参数类型 。我认为你提到的答案结尾的理由总结得很好。因为类型推断不能总是从方法组隐式地进行,所以编译器甚至不按照规范尝试它。
答案 3 :(得分: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
答案 4 :(得分:0)
以下也有效;我不知道为什么:
var outputs = Map(inputs, i => Square(i));