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

时间:2010-01-21 04:22:03

标签: c# .net generics c#-3.0 type-inference

假设我有

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

5 个答案:

答案 0 :(得分:10)

从错误消息中:

  

无法从用法中推断出方法“[...].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);

是合法的。在这种情况下,它已经解决了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);

我不知道为什么 - 也许从SquareFunc<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));