C#中通过“动态重载”实现单/双重分派

6

为什么基于参数类型的“动态重载”不能被C#原生支持?我认为这需要动态分派,但由于虚拟方法调用也是动态分派,因此这对语言来说并不奇怪。那么为什么这个特性不是C#的一部分呢?使用反射实现此功能的最优雅解决方案是什么(也许有一些库可以使用)?

class Program
{
    static void Main(string[] args)
    {
        var objs = new object[] { new Class1(), new Class2() };
        foreach (var item in objs)
        {
            Method(item);
        }
    }

    static void Method(Class1 obj)
    {
    }

    static void Method(Class2 obj)
    {
    }
}

class Class1
{
}

class Class2
{
}
更新,确实,因为在这个例子中Method不是虚拟的,并且只有一个参数,所以这仍然是单分派,但是分派的基数并不重要,只要它> 0。

通过模板/泛型,C#/C++和Java至少提供了某种形式的参数化多态性。 - Gonen I
3个回答

10

virtual关键字用于单重分派。如果您需要进行双重分派,可以在C#中使用以下方式:

virtual 适用于单一派发。如果需要双重派发,则可在 C# 中使用以下代码:

(Note: The first sentence could also be translated as "virtual关键字用于单分派" depending on the context, but I chose to use "单重分派" to match the English text more closely.)
var objs = new object[] { new Class1(), new Class2() };
foreach (var item in objs)
{
    Method((dynamic)item);
}

这将使编译器以非常不同的方式解析对您方法的调用。它将发出一个所谓的调用站点,在运行时确定应调用哪个方法。这也被称为后期绑定

在这个特定的例子中,您仍然会得到单一分派,但如果Method是虚拟的,则会变成双重分派。

这非常方便,可以快速实现访问者模式,但请注意,这比经典的手动双重分派要慢。因此,您可能希望在性能敏感的代码中使用传统的方式。


在这个例子中,从技术上讲它仍然是单分派。但如果“Method”是虚拟的,它就会变成双重分派。 - Lucas Trzesniewski
我认为正确的术语是参数多态性。 - Gonen I
好的,已将参数多态标签添加到问题中。 - Pol

4
为什么基于参数类型的“动态重载”双重派发在C#中没有本地支持? 通过动态类型,是有支持的。
static void Main(string[] args)
{
    var objs = new object[] { new Class1(), new Class2() };
    // Note the change of type for item to "dynamic"
    foreach (dynamic item in objs)
    {
        Method(item);
    }
}

使用编译时类型为dynamic的值进行操作是后期绑定的,因此重载决议是基于值的实际类型在执行时进行的。当然,动态类型有更多的内容,包括类型能够通过代码动态提供成员。
这一切都会带来性能损失 - 但有时候这是最清晰的方法。
您可能希望添加一个额外的重载,其参数类型为object,作为“万能”的重载以防值不匹配其他重载之一…尽管如果数组包含空元素,则仍然会产生歧义。

1
我不确定我是否理解这种方法。你的意思是 Method 会被调用两次,一次是针对 Method(Class1),另一次是针对 Method(Class2) 吗? - Matías Fidemraizer
1
@MatíasFidemraizer:是的,没错。它将根据值的执行时类型选择要调用的重载函数。 - Jon Skeet
1
好的,我理解了。我误认为双重分发是通过调用“方法”一次来解决两个重载函数。无论如何,没事,我有点儿疯了:D - Matías Fidemraizer

3
如果您真的感兴趣,.Net CLR泛型设计师之一Andrew Kennedy写了一篇关于此的文章。

http://research.microsoft.com/pubs/64039/transposingftocsharp.pdf

自从F#使用真正的参数化多态性,它使用相同的公共语言运行时,据推测C#也可以实现它,而决策是为了保留C ++ / C#语言风格。
从他的文章中可以看出:“同样,多态虚方法的CLR实现[10]涉及执行时代码生成,与支持非虚拟多态方法足够的加载时代码生成形成对比。”

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接