2013-12-18 165 views
9

我已经进行了如下推理测试:为什么C#无法从非泛型静态方法的签名推断泛型类型参数类型?

static class InferenceTest { 
    static void TakeInt(int a) { } 
    static int GiveInt() { return 0; } 
    static int TakeAndGiveInt(int a) { return 0; } 

    static void ConsumeAction1<T>(Action<T> a) { } 
    static void ConsumeFunc1<T>(Func<T> f) { } 
    static void ConsumeFunc2a<T1, T2>(Func<T1, T2> f) { } 
    static void ConsumeFunc2b<T>(Func<int, T> f) { } 
    static void ConsumeFunc2c<T>(Func<T, T> f) { } 
    static void ConsumeFunc1Func2<T1, T2>(Func<T1> f1, Func<T1, T2> f2) { } 

    static void Main() { 
     ConsumeAction1(TakeInt);  //error 
     ConsumeFunc1(GiveInt);   //ok 
     ConsumeFunc2a(TakeAndGiveInt); //error 
     ConsumeFunc2b(TakeAndGiveInt); //ok 
     ConsumeFunc2c(TakeAndGiveInt); //error 
     ConsumeFunc1Func2(GiveInt, TakeAndGiveInt); //ok 
    } 
} 

结果似乎表明,C#编译器无法推断通用类型参数从非泛型方法组委托函数的参数。

我什么困扰最深的是,C#是可以推断Func<T1, T2>从方法的返回值类型变量在ConsumeFunc1Func2,但无法推断类型Func<T, T>ConsumeFunc2c

这个问题类似于T of Func<S, T> is inferred from output of lambda expression only when S and T are different?的问题,但不是具有未知参数类型的lambdas,我们有非泛型方法组。

Why can't C# infer type from this seemingly simple, obvious case问题的答案类型的问题“为什么非模糊的非泛型方法不足以进行推理?”和“为什么参数类型和推理的返回值类型之间存在差异?”。

问题:

为什么C#编译器推断Func<T>使用的返回值的类型的类型,但没有看到Func<T, T>情况下成功?

为什么C#编译器推断从Func<T1>Func<T1, T2>T1类型参数在ConsumeFunc1Func2,但不能推断ConsumeFunc2cFunc<T, T>从自身T型参数,它似乎是更容易?

+0

在ConsumeFunc1Func2中,编译仍然只从返回值推断,而不是参数类型。 T1从GiveInt的返回值中解析出来,T2从TakeAndGiveInt的返回值中解析出来。因此,ConsumeFunc1Func2案例没有增加额外的秘密。 – Baldrick

+1

我会很好的阅读C#4.0规范的第7.5.2节。它非常易读,描述了类型推断的各个阶段,以及它们与方法组的关系。 – Baldrick

+0

'ConsumeFunc2b'表明''Func '返回类型'T'可以从'TakeAndGiveInt'解析。但是当'?'也是'T'时,'ConsumeFunc2c'中的'Func '就是这种情况,编译器似乎忘记了参数'T'与已经推断的'T'相同。与'ConsumeFunc1Func2'成功不同。 –

回答

1

方法参数未被检查。

如上所述,在ConsumeFunc1Func2中,编译器仅从返回值推断。 在ConsumeFunc2c中,TakeAndGiveInt签名不被检查,看它的方法参数类型是否实际上是相同类型的方法返回类型原因...方法参数未被检查!

1

通常,方法名称不会唯一标识方法组可以分配到的唯一类型Action<T>。例如,即使只有一个过载Fred并且需要一个Cat参数,那么该过载不仅可以分配给Action<Cat>,还可以分配给其他类型,如Action<Mammal>Action<Animal>Action<Object>。虽然在某些情况下,一种类型的替代在各方面都会优于其他替代品,但并非总是如此。定义语言以要求指定委托类型更清晰,而不是让编译器试图“猜测”,特别是因为让编译器猜测意味着许多不应该突破变化的事情会是(例如添加一个方法重载可能会导致用于工作的类型推断模糊不清)。