Expression<Action> 和 Expression<Action<T> 之间的重载优先级>

Overload precedence between Expression<Action> and Expression<Action<T>>

简短版本:

重载两个方法的最佳方式是什么,一个接受 Expression<Action>,另一个接受 Expression<Action<T>>

较长的版本:

假设我在(设计糟糕的)class 中有以下方法:

void Main()
{
    Foo(t => Bar(t));
}

void Foo(Expression<Action> action)
{
    "Method 1!".Dump();
}

void Foo<T>(Expression<Action<T>> action)
{
    "Method 2!".Dump();
}

void Bar(String thing)
{
    // Some bar-like thing
}

现在,可能是我特别模糊,但我希望 'Method 2' 从 Main 方法中调用。

唯一的限制是我需要传递一个 Expression<...> 因为我们在其他地方根据表达式树做一些魔术。

我的理由是这样的:

  1. 泛型不是真的 - 它们是编译器的把戏
  2. Action<T> 实际上是与 Action
  3. 截然不同的委托类型
  4. 因此应该调用方法 2,因为编译器拥有进行推理所需的所有信息。

实际发生的是我收到一个编译器错误,大意是我试图将一个参数传递给一个不接受任何参数的 Action...即。 Method 1 正在被定位。

附带说明一下,如果我像这样明确指定通用参数,这将按预期工作:

Foo<String>(t => Bar(t));

我们将不胜感激您对此的看法!

目前,您的方法都不适用 - 因为类型推断无法为您的第二种方法推断 T,并且第一种方法无效,因为您的匿名函数有一个参数(不像 Action).编译器正在报告一个错误,就好像它已经执行了重载解析并选择了第一个方法,但这是错误消息并没有真正说明全部情况的情况之一。

如果将方法一的签名改成:

Foo(Expression<Action> action, string item)

和方法2的签名为:

Foo<T>(Expression<Action<T>> action, T item)

那么类型推断就会起作用,并且会调用第二种方法(因为第一种方法不适用)。

如果两种方法都适用(例如,在上述更改后,您将非泛型方法的第一个参数更改为Expression<Action<string>>),它将以根据正常 "argument to parameter type" 转换的决胜局 - 但第一个决胜规则(在 C# 5 规范的第 7.5.3.2 节中)是:

  • If MP is a nongeneric method and MQ is a generic method, then MP is better than MQ.

换句话说,在重载决策中,非泛型方法优于泛型方法。

就修复当前代码而言 - 如果不了解您要实现的目标的更多上下文,就很难知道如何去做。