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

C# .NET:静态方法上的推断泛型类型,c#,.net,generics,c#-3.0,type-inference,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

如果我有

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);
}
公共静态列表映射(列表输入,函数f)
{
返回输入。ConvertAll((x)=>f(x));
}
私有整数平方(整数x){return x*x;}
公开募捐
{
变量输入=新列表(新的int[]{2,4,8,16,32,6412825651210242048});
//这是不可编译的
var输出=映射(输入,平方);
//这很好
var输出2=映射(输入,平方);
//这也很好(谢谢,杰森)
var输出2=映射(输入,(x)=>x*x);
//也很好
var输出2=映射(输入,(x)=>x*x);
}
为什么它不编译

编辑:错误为:

错误CS0411:无法从用法推断方法“Namespace.Map(System.Collections.Generic.List,System.Func)”的类型参数。尝试显式指定类型参数

为什么我必须指定Map()函数的类型?它不能从传递的
Func
推断出这一点吗?(就我而言,是Square)


答案与

推断委托类型失败,而不是列表:

// 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);
//这也很好
var outputs3=映射(输入,新函数(平方));
//更多正确编译的调用
var输出4=映射(输入,x=>平方(x));
var输出5=映射(输入,x=>x*x);
Func t=平方;
var输出6=映射(输入,t);

但是我不知道为什么——也许从
Square
的签名到
Func
,没有隐式的类型转换?似乎很奇怪,
Func t=Square
是有效的,但编译器无法自行实现飞跃。。。Bug,也许吧?

以下功能也可以使用;我不知道为什么:

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

经过一番调查,我发现你对另一个答案的怀疑是正确的。以下是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) 对于相应的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
Func f=Square;
//之所以有效,是因为我们提供了目标类型
//从正方形到委托的转换
映射(输入,i=>平方(i));
//之所以有效,是因为lambda遵循实际的方法调用
//并确定其自己的返回类型

从错误消息中:

无法从用法推断方法“
[…]”Map(System.Collections.Generic.List,System.Func)
”的类型参数。尝试显式指定类型参数

请注意,错误消息表示它无法找出类型参数。也就是说,它在解析类型参数
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));
}
这是合法的。在这种情况下,已经解决了
T
int
,因为
inputs
列表

现在,更深层次的问题是,为什么上面的规范是这样的?也就是说,为什么方法组在类型参数解析中不起作用?我想是因为这样的情况:

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?
    }
}
类程序{
公共静态TM(函数f){
返回默认值(T);
}
公共静态整数F(整数i){
返回i;
}
公共静态浮点F(浮点F){
返回f;
}
静态void Main(字符串[]参数){
M(F);//我是哪个F?
}
}

很有趣。您介意发布编译器错误吗?如果您将Run()的第一行中的var更改为List,结果会是什么?@tehMick:结果将是相同的。已推断类型。错误1无法从用法推断方法“WindowsFormsApplication1.Class4.Map(System.Collections.Generic.List,System.Func)”的类型参数。尝试显式指定类型参数。方法参数的IntelliSense正在解析第一个类型参数,而不是第二个类型参数。它显示:List Class4.Map(List inputs,Func f)。这有什么意义吗?请阅读错误消息。方法“
[…].Map(System.Collections.Generic.List,System.Func)
”的类型参数。它无法确定泛型类型参数是什么。它可以解析
T
,因为
inputs
是一个
列表
,所以它知道
T
int
。因此,它无法解析
T2
。看看我的答案。我不明白这个答案是怎么得出的;这是错误的。它在推断委托类型方面并没有失败。这是因为它无法使用方法组来推断其中一个类型参数。委托要求T2作为泛型参数,而编译器无法推断T2的类型(根据规范,正如您所指出的),因此编译器无法推断委托的类型。
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?
    }
}