为什么C#无法从非泛型静态方法的签名推断出泛型类型参数类型?

问题描述 投票:10回答:2

我进行了以下推理测试:

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?问题相似,但我们具有非泛型方法组,而不是具有未知参数类型的lambda。

Why can't C# infer type from this seemingly simple, obvious case问题回答了以下问题:“为什么无歧义的非泛型方法不足以进行推理?”和“为什么参数类型和推理的返回值类型之间有区别?”

问题:

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

为什么C#编译器可以从T1中的Func<T1, T2>推断出Func<T1>ConsumeFunc1Func2类型实参,却无法从T中的自身推断出Func<T, T>ConsumeFunc2c类型实参。更容易吗?

c# generics type-inference
2个回答
2
投票

[通常,方法名称不会唯一地标识可以向其分配方法组的唯一类型Action<T>。例如,即使Fred只有一个重载并且只接受一个Cat参数,该重载不仅可以分配给Action<Cat>,而且还可以分配给某些其他类型,例如Action<Mammal>Action<Animal>Action<Object>。虽然在某些情况下,一种类型替换在任何方面都将优于任何其他替换,但情况并非总是如此。定义语言来要求指定委托人的类型比让编译器尝试“猜测”要干净得多,尤其是因为让编译器进行猜测将意味着很多本不应该破坏更改的事情(例如添加方法重载可能会使用于工作的类型推断变得含糊不清。]


1
投票

未检查方法参数。

根据建议,在ConsumeFunc1Func2中,编译器仅从返回值进行推断。在ConsumeFunc2c中,不检查TakeAndGiveInt签名以查看其方法参数类型是否实际上与方法返回类型的类型相同,原因是未检查方法参数!

© www.soinside.com 2019 - 2024. All rights reserved.