为什么函数签名相同的委托不是同一个类型

我们知道,在 C# 中,同一个签名定义的委托是不同的类型,例如 delegate int MyDelegate(int arg0); 定义的 MyDelegateFunc<int, int> 不是同一个委托类型,并且它们之间没有隐式转换

讨论缘起于这个 知乎回答,大概是答主对 C# 的委托机制不太了解,以为 C# 的委托是值捕获的(是引用捕获,实现上等价于把被捕获的东西放入一个对象中,然后以这个对象的方法产生一个委托)。在评论的讨论中,作者提问如何让一个匿名方法返回自己,以及如何让两个匿名方法互相返回,答案是容易尝试的:

class Program
{
    delegate D D();
    delegate E E();
    delegate U V();
    delegate V U();

    static void Main()
    {
        D d = null, du = null, dv = null;
        d = () => d;
        du = () => du;
        dv = () => dv;
        U u = null; V v = null;
        u = () => v;
        v = () => u;
    }
}

现在我们回过头来看,如果用签名等价的委托类型思想,EDUV 都是 T = Func<Func<T>> 的解。而且 ED 都是 T = Func<T> 的解。

用数学的观点看,若 ff00 映为 ff,则 ff 已经没法表示成集合(映射是集合间的一种特别的关系)的样子了,对于我(没有学习过类型论、集合论)来说,这太难以分析了。(注:要证明 ff 不是集合,只要注意到集合不能拥有自己,也不能拥有拥有自己的集合,也不能拥有拥有拥有自己的集合。)

另一种理解为什么签名相同的委托不是同一个类型的别名,可以参见 vczh 的评论:

delegate Fuck Fuck(); 等价于 Java 中的

interface Fuck
{
    Fuck Invoke();
}

同理,我们很难说

interface I1
{
    int Invoke();
}
interface I2
{
    int Invoke();
}

定义了同一个接口。此外,在 C# 里面你可以做 C++ 里面 std::function 模板做不到的事情(例如 EDUV 这几个类型没法写成有限的 std::function 模板实例化,除非再定义一个可以转换到 std::function 的具名类型),因此也没有理由默认委托只是函数签名。

请启用 JavaScript 来查看由 Disqus 驱动的评论。