C#是单重派发还是多重派发语言?

32

我正在尝试准确理解"单分派"和"多分派"是什么意思。

我刚刚阅读了这篇文章:
http://en.wikipedia.org/wiki/Multiple_dispatch

从那里的定义来看,似乎C#和VB.Net属于多分派,尽管选择调用哪个重载是在编译时确定的。

我这样理解是否正确?还是我漏掉了什么? 谢谢!

8个回答

50

好的,我理解函数重载与多分派之间微妙的区别。

基本上,区别在于选择调用哪个方法是在运行时还是编译时决定的。现在,我知道每个人都说过这个,但是没有明确的例子,这听起来非常显然,因为C#是静态类型语言,而多分派语言(至少对我来说)似乎是动态类型。到目前为止,仅凭那个定义,多分派和函数重载对我来说完全相同。

这种情况真正产生影响的地方是当您:

  • 有两个方法重载,它们在参数类型上不同 (CaptureSpaceShip(IRebelAllianceShip ship)CaptureSpaceShip(Xwing ship))
  • 这两种类型 (IRebelAllianceShipCaptureSpaceShip) 是多态的,并且
  • 您使用声明为更高类型的引用调用该方法,实际上指向较低类型的对象

完整示例:

int CaptureSpaceShip(IRebelAllianceShip ship) {}
int CaptureSpaceShip(XWing ship) {}

void Main() { 
  IRebelAllianceShip theShip = new XWing();
  CaptureSpaceShip(theShip);
}

XWing 显然实现了 IRebelAllianceShip 接口。 在这种情况下,将调用第一个方法,而如果 C# 实现了多重分派,将调用第二个方法。
抱歉文档重复...这似乎是解释这种差异最清晰的方式,而不仅仅是阅读每个分派方法的定义。
更正式的解释请参见: http://en.wikipedia.org/wiki/Double_dispatch#Double_dispatch_is_more_than_function_overloading

这是一个很好的主要区别示例。 - Paul Stovell
谢谢!我需要这样的一个例子。 - John
1
@Daniel Magliola,请注意,通过利用动态特性,C# 4.0现在可以实现动态分派。 - RichardOD
1
"多重分派语言似乎是动态类型的。"在阅读了http://www.perl.com/pub/2007/12/06/soto-11.html?page=1之后,我认为实际上恰恰相反(请参见他说“将类型放入Perl 6的主要原因不是强类型,而是多重分派”的部分)。如果您要根据参数类型决定调用哪个方法,则可能需要具有显式类型的方法定义,这是动态类型语言通常不具备的(至少我见过的语言如此)。" - dsetton
@David Mulford的回答(新的dynamic关键字)应该是正确的答案 - 也就是说,如果你想让CLR进行动态分派(我猜JVM的invoke-dynamic类似,但它不适用于Java) - Ustaman Sangat
澄清一下:CaptureSpaceShip((IRebelAllianceShip)theShip) 调用 IRebelAllianceShip 重载(与 theShip 的值无关),但在这里强制转换是不必要的,因为变量已经被静态声明为 IRebelAllianceShipCaptureSpaceShip((XWing)theShip) 调用 XWing 重载(或者如果 theShip 不是 XWing 则崩溃)。在 C# 4.0+ 中,CaptureSpaceShip((dynamic)theShip) 调用最具体的适用重载(或者如果两个适用重载同样具体,则崩溃)。 - Bjartur Thorlacius

30

对于通过搜索引擎找到这篇文章的读者,C# 4.0 引入了 dynamic 关键字。代码如下所示。

int CaptureSpaceShip(IRebelAllianceShip ship) {}
int CaptureSpaceShip(XWing ship) {}

void Main() {   
    IRebelAllianceShip theShip = new XWing();  
    CaptureSpaceShip((dynamic)theShip);
}

7
C# 4.0支持多重分派。低于该版本的版本不支持。 - RichardOD
2
它被称为动态分派。多重分派只是能够进行动态参数类型解析的副产品。 - Dmitri Nesteruk

7

C#是单分派语言,但有些博客文章的标题看起来像是在模拟多态方法。如果我能加载其中一篇文章,我会在这里更新我的答案。


4

也许有些人会对使用动态关键字的多重调度的好的C#实例感兴趣(MSDN博客

class Animal 
{ 
}

class Cat : Animal 
{ 
}

class Dog : Animal 
{ 
}

class Mouse : Animal 
{ 
}

我们可以创建同一个方法的多个重载,根据不同参数类型的组合进行专门化:
void ReactSpecialization(Animal me, Animal other) 
{ 
    Console.WriteLine("{0} is not interested in {1}.", me, other); 
}

void ReactSpecialization(Cat me, Dog other) 
{ 
    Console.WriteLine("Cat runs away from dog."); 
}

void ReactSpecialization(Cat me, Mouse other) 
{ 
    Console.WriteLine("Cat chases mouse."); 
}

void ReactSpecialization(Dog me, Cat other) 
{ 
    Console.WriteLine("Dog chases cat."); 
}

现在是有趣的部分:

void React(Animal me, Animal other) 
{ 
    ReactSpecialization(me as dynamic, other as dynamic); 
}

这是因为"as dynamic"的转换作用,告诉C#编译器不仅要调用ReactSpecialization(Animal, Animal),还需要动态检查每个参数的类型并在运行时选择要调用哪个方法重载。
为了证明它确实有效:
void Test() 
{ 
    Animal cat = new Cat(); 
    Animal dog = new Dog(); 
    Animal mouse = new Mouse();

    React(cat, dog); 
    React(cat, mouse); 
    React(dog, cat); 
    React(dog, mouse); 
}

输出:

Cat runs away from dog.
Cat chases mouse.
Dog chases cat.
Dog is not interested in Mouse.

维基百科称,C# 4.0 (dynamic)是一种"多重分派"语言。
我认为Java、C#(4.0之前)和C++等语言都是单向分派。

2

C#不支持多重分派。访问者设计模式模拟了可以描述为多重分派的东西,尽管访问者模式主要专注于将算法与层次结构分离。


1
根据引用维基百科文章,多重分派的定义是基于涉及对象的运行时类型,因此C#和VB.net不使用它,因为决策是在编译时进行的,正如您所述。

0
GoF访问者模式是如何执行双重分派的一个例子。Scott Meyers的《More Effective C++》向您展示如何在C++中执行它。这里有一篇来自Dr Dobbs的链接,介绍了如何在Java和C++中执行双重分派。

0

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