编译器没有绑定到正确的泛型方法重载

Hap*_*mad 0 c# overloading type-inference compiler-errors

通常,C#编译器对方法绑定和类型参数推断很聪明.但我似乎已经难倒了.

class Obj
{
    void Handler( int a, int b ) { }

    Obj() { Method( "", Handler ); }

    public void Method<T>( T t, Action<T> action ) { }

    public void Method<T, U>( T t, Action<U, U> action ) { }
}
Run Code Online (Sandbox Code Playgroud)

Method调用导致编译器错误:

参数2:无法从'方法组'转换为'System.Action'.

为什么编译器没有注意到调用符合第二个重载?我可以通过使调用更明确,如在Method<string, int>( "", Handler )或中编译它Method( "", (Action<int, int>)Handler ).但为什么这有必要呢?

Eri*_*ert 6

让我们看看安东尼的建议,并考虑一下:

class Obj
{
    void Handler( int a, int b ) { }
    Obj() { Method( "", Handler ); }
    public void Method<T, U>( T t, Action<U, U> action ) { }
}
Run Code Online (Sandbox Code Playgroud)

重载解析失败.为什么?好吧,我们必须推断T和U.显然T是字符串.什么是U?

这一点很重要:在我们知道Handler是什么之后,我们推断出U是什么.现在,您可能会说我们知道Handler是什么,因为它只有一件事.但是没有C#规则说如果方法组中只有一个方法可以自动赢得重载决策游戏.规则是Handler的含义是通过在与Handler关联的方法组上运行重载决策来确定的.重载决策考虑了参数,我们没有Handler的任何参数,因为我们可能拥有的唯一参数列表是(U,U),而U是我们首先想要确定的.

因此,重载决议失败了.现在,如果我们有:

class Obj
{
    double M(string s) { }
    Obj() { Method( "", M ); }
    public void Method<T, U>(T t, Func<T, U> f) { }
}
Run Code Online (Sandbox Code Playgroud)

这很好.我们推断T是字符串,现在我们可以对M进行重载决策,确定M意味着double M(string s),现在我们知道U是双精度的.