为什么不能将 Action<Object> 委托用作线程池的 WaitCallback?

Why can't an Action<Object> delegate be used as a WaitCallback for Threadpool?

代表 WaitCallbackAction<Object> 共享相同的签名。尽管如此,正如下面的代码所证明的,它们不能被视为同一类型。

static void Main(string[] args)
{
    WaitCallback wcb = (o) => Console.WriteLine("Hello from wcb delegate");
    ThreadPool.QueueUserWorkItem(wcb);//works

    Action<object> action = (o) => Console.WriteLine("Hello from action<object> delegate");
    ThreadPool.QueueUserWorkItem(action);//does not compile: Cannot convert from Action<object> to WaitCallback  

    Console.ReadLine();
}

关于这个观察我有两个问题:

  1. 为什么签名相同但名称不同的代表不属于同一类型?语言设计团队是否会选择不同的选择,或者会导致不一致或其他问题?

  2. 当在 .NET Framework 2.0 中引入 Action 委托时,WaitCallback 已经存在。我想知道是否不是这种情况,是否有理由引入额外的委托类型 WaitCallback 而不是使用 Action

从类型系统的角度来看,WaitCallbackAction<object> 是不同的类型,它们之间不存在隐式转换。因此,您不能在需要 WaitCallback 的地方使用 Action<object>。然而,实际的 方法 被引用并用作委托,只关心它们的参数列表和 return 类型的兼容性,所以你可以 [=25= 而不是匿名方法]

private void Foo(object o) {
  Console.WriteLine("Hello from actual method");
}

下面几行可以正常工作:

WaitCallback wcb = Foo;
Action<object> action = Foo;
ThreadPool.QueueUserWorkItem(Foo);

请注意,在这种情况下,方法的 name 用于引用它,其解析和处理方式与将特定类型的变量传递给方法完全不同。对于后者,您完全受实际类型的支配,它们的转换和委托绝不是特殊的。

规范还包含以下注释:

Delegate types in C# are name equivalent, not structurally equivalent. Specifically, two different delegate types that have the same parameter lists and return type are considered different delegate types.

(警告:猜测;我不在语言设计团队) 所以这是一个有意识的选择,考虑到我上面写的,这可能不是不可能的允许您请求的内容,但这将是重载解析工作方式的一个主要例外。所以我猜这个电话是为了简化语言。此外,在 C# 1 时代,实际上结构兼容的委托可能并不多。

至于你的第二点,在那种情况下他们可能会选择 Action。例如,请参阅 Task.Run 方法,它采用 Action 而不是自定义委托。对于不采用或采用一个参数的操作,似乎很明显不引入新委托类型的情况。但是,对于 Action<string, string, int, double> 或等效的 Func 类型,我猜想拥有一个可以记录的实际命名类型将比使用通用 ActionFunc 类型好得多。