多分派

多分派或译多重派发（multiple dispatch）或多方法（multimethod），是某些编程语言的一个特性，其中的函数或者方法，可以在运行时间（动态的）基于它的实际参数的类型，或在更一般的情况下于此之外的其他特性，来动态分派。这是对单分派多态的推广， 那里的函数或方法调用，基于在其上调用方法的对象的派生类型，而动态分派。多分派使用一个或多个实际参数的组合特征，路由动态分派至实现函数或方法。
理解分派.
软件工程师通常把代码写进代码块中，代码块通常称作过程、函数、方法。代码通过被调用来执行，调用时将控制权传入函数中，当函数执行完成后将控制权返回给调用者。
函数名通常用来描述函数的目的。有时会将多个函数起同样的名称。比如同名函数在逻辑上处理相同的任务，但是操作在不同类型的输入值上。在这种情况下，无法仅仅通过函数名，来判断目标代码块。那么，函数的实际参数的个数和类型，也就被用来判断。
通常，单分派面向对象语言，在调用一个方法时，方法参数中一个参数会被特殊对待，并用来决定哪一个方法（如果有多个同名方法）会被调用。在许多语言中，这个特殊的参数是在语法上指明的，许多编程语言在调用方法时，把特殊参数放在小圆点（codice_1）之前。例如 codice_2，这样 codice_3 将会发出狮吼，同时 codice_4 只会吱吱地叫。一般来说，对于面向对象的编程语言，这个小圆点之前的参数（上例中的codice_5和codice_6）被称为接收者。
相反，在实现了多分派的语言中，被调用的函数，即是那些参数个数一样多，并且类型也匹配的调用。在调用中并没有特殊参数，来决定那个方法被调用。也就是说，所有参数的运行时类型都参与分派。CLOS是早期和著名的多分派语言。
数据类型.
对于编译时间可以区分数据类型的编程语言，在（alternative）函数中进行选择，可以发生在编译时间，创建交替函数用于编译时间选择的活动，通常被叫做函数重载。
在有些编程语言中，这种数据类型的识别，可以被延后至运行时间（）。交替函数的选择发生在运行时间，并依据动态确定的函数实际参数的类型。以这种方式选择交替实现的函数，通常被称为多方法。
例子.
可以通过例子更加清晰的区分多分派和单一分派。假想一个游戏，它有两种（用户可见的）物体：飞船和小行星。当两个物体要相撞的时候，程序需要依据什么物体要相撞而做不同的事情。
内建多分派.
C#.
C#在版本4（2010年4月），使用关键字codice_7，介入了对动态多方法的支持。下面的例子展示多方法。像很多其他静态类型的语言语言一样，C#还支持静态方法重载，Microsoft预期开发者在多数场景下会选用静态类型超过动态类型。codice_7关键字支持COM对象和动态类型的.NET语言的互操作。
下面的例子使用了C# 9和C# 10介入的特征比如记录类型：
using static ColliderLibrary;
Console.WriteLine(Collide(new Asteroid(101), new Spaceship(300)));
Console.WriteLine(Collide(new Asteroid(10), new Spaceship(10)));
Console.WriteLine(Collide(new Spaceship(101), new Spaceship(10)));
string Collide(SpaceObject x, SpaceObject y) =&gt;
 x.Size &gt; 100 &amp;&amp; y.Size &gt; 100
 ? "Big boom!"
 : CollideWith(x as dynamic, y as dynamic); // Dynamic dispatch to CollideWith method
class ColliderLibrary {
 public static string CollideWith(Asteroid x, Asteroid y) =&gt; "a/a";
 public static string CollideWith(Asteroid x, Spaceship y) =&gt; "a/s";
 public static string CollideWith(Spaceship x, Asteroid y) =&gt; "s/a";
 public static string CollideWith(Spaceship x, Spaceship y) =&gt; "s/s";
abstract record SpaceObject(int Size);
record Asteroid(int Size) : SpaceObject(Size);
record Spaceship(int Size) : SpaceObject(Size);
输出：
Big boom!
a/s
s/s
Common Lisp.
在具有多分派的Common Lisp语言中，可以在Common Lisp对象系统中如下这样实现：
 (if (and (&gt; (size x) 100) (&gt; (size y) 100))
 "big-boom"
 (collide-with x y)))
并且对其他方法也是类似的。没有使用显式测试和“动态转换”。
由于多分派的存在，方法要定义在类中并包含在对象中的传统想法，变得不再吸引人了，上述每个codice_9方法，都附属于两个不同的类而非一个类。因此方法调用的特殊语法，一般会消失，从而方法调用看起来完全就像正常的函数调用，并且方法被组织入泛化函数而非类中。
Julia.
Julia有内建的多分派，并且它是语言设计的中心。Julia版本的例子如下：
abstract type SpaceObject end
struct Asteroid &lt;: SpaceObject
 size::Int 
end
struct Spaceship &lt;: SpaceObject
 size::Int 
end
collide_with(::Asteroid, ::Spaceship) = "a/s"
collide_with(::Spaceship, ::Asteroid) = "s/a"
collide_with(::Spaceship, ::Spaceship) = "s/s"
collide_with(::Asteroid, ::Asteroid) = "a/a"
collide(x::SpaceObject, y::SpaceObject) = (x.size &gt; 100 &amp;&amp; y.size &gt; 100) ? "Big boom!" : collide_with(x, y)
输出：
julia&gt; collide(Asteroid(101), Spaceship(300))
"Big boom!"
julia&gt; collide(Asteroid(10), Spaceship(10))
"a/s"
julia&gt; collide(Spaceship(101), Spaceship(10))
"s/s"
Groovy.
Groovy是兼容/互用于Java的通用的JVM语言，它对立于Java，使用后期绑定/多分派。
/*
 Groovy implementation of C# example above
 Late binding works the same when using non-static methods or compiling class/methods statically
 (@CompileStatic annotation)
class Program {
 static void main(String[] args) {
 println Collider.collide(new Asteroid(101), new Spaceship(300))
 println Collider.collide(new Asteroid(10), new Spaceship(10))
 println Collider.collide(new Spaceship(101), new Spaceship(10))
class Collider {
 static String collide(SpaceObject x, SpaceObject y) {
 (x.size &gt; 100 &amp;&amp; y.size &gt; 100) ? "big-boom" : collideWith(x, y) // Dynamic dispatch to collideWith method
class SpaceObject {
 int size
库扩展多分派.
在不于语言定义或语法层次支持多分派的语言中，可能经常使用扩展库来增加多分派。
Python.
Python可以使用库扩展来增加多分派。例如，最早的模块multimethods.py，提供了CLOS风格的多方法而不用变更语言的底层语法或关键字。Guido van Rossum使用Python 2.4介入的修饰器（decorator），建立了具有简化语法的多方法的简单实现。multipledispatch库采用的形式与之一致。
模块multimethod，采用了修饰器和Python 3.5介入的类型提示实现多方法，下面的多方法例子还基于了Python 3.10介入的联合类型（codice_10）：
from multimethod import multimethod
class Asteroid():
 def __init__(self, size=0):
 self.size = size
class Spaceship():
 def __init__(self, size=0):
 self.size = size
SpaceObject = Asteroid | Spaceship
@multimethod
def collide_with(x :Asteroid, y :Asteroid):
 return "a/a"
@multimethod
def collide_with(x :Asteroid, y :Spaceship):
 return "a/s"
@multimethod
def collide_with(x :Spaceship, y :Asteroid):
 return "s/a"
@multimethod
def collide_with(x :Spaceship, y :Spaceship):
 return "s/s"
def collide(x :SpaceObject, y :SpaceObject):
 assert isinstance(x, SpaceObject) \
 and isinstance(y, SpaceObject) 
 print("Big boom!" if x.size&gt;100 and y.size&gt;100
 else collide_with(x, y))
其执行：
»&gt; collide(Asteroid(101), Spaceship(300))
Big boom!
»&gt; collide(Asteroid(10), Spaceship(10))
a/s
»&gt; collide(Spaceship(101), Spaceship(10))
s/s
plum库也实现了这种形式的多分派。
JavaScript.
JavaScript和TypeScript不在语言语法层次上支持多方法，但可以通过库扩展来增加多分派。例如，使用multimethod包，它提供了多分派、泛化函数的实现。这个库的JavaScript的动态类型版本例子：
import { multi, method } from '@arrows/multimethod'
const collideWith = multi(
 method([Asteroid, Asteroid], (x, y) =&gt; {
 // deal with asteroid hitting asteroid
 method([Asteroid, Spaceship], (x, y) =&gt; {
 // deal with asteroid hitting spaceship
 method([Spaceship, Asteroid], (x, y) =&gt; {
 // deal with spaceship hitting asteroid
 method([Spaceship, Spaceship], (x, y) =&gt; {
 // deal with spaceship hitting spaceship
)
这个库还有TypeScript有对应的静态类型版本。
模拟多分派.
C.
C语言没有动态分派，它必须手工的以某种形式实现。经常使用一个codice_11来识别一个对象的子类型。动态分派可以在一个函数指针分支表中查找这个值来完成。下面是采用C语言的简单例子：
typedef void (*CollisionCase)(void);
void collision_AA(void) 
void collision_AS(void)
void collision_SA(void)
void collision_SS(void)
typedef enum {
 THING_ASTEROID = 0,
 THING_SPACESHIP,
 THING_COUNT /* 它自身不是事物类型，转而用来表示事物的数目 */
} Thing;
CollisionCase collisionCases[THING_COUNT][THING_COUNT] = {
 {&amp;collision_AA, &amp;collision_AS},
void collide(Thing a, Thing b)
 (*collisionCases[a][b])();
int main(void) 
 collide(THING_SPACESHIP, THING_ASTEROID);
C语言使用C Object System库，可以支持类似于CLOS的动态分派。它是完全可扩展的并且方法不需要任何的手工处理。动态消息（方法）通过COS分派器来分派，它比Objective-C更快。
Java.
在Java这种只有单分派但支持方法重载的语言中，多分派可以通过多层单分派来模拟：
interface Collideable {
 void collideWith(final Collideable other);
 /* These methods would need different names in a language without method overloading. */
 void collideWith(final Asteroid asteroid);
 void collideWith(final Spaceship spaceship);
class Asteroid implements Collideable {
 public void collideWith(final Collideable other) {
 // Call collideWith on the other object.
 other.collideWith(this);
 public void collideWith(final Asteroid asteroid) {
 // Handle Asteroid-Asteroid collision.
 public void collideWith(final Spaceship spaceship) {
 // Handle Asteroid-Spaceship collision.
class Spaceship implements Collideable {
 public void collideWith(final Collideable other) {
 // Call collideWith on the other object.
 other.collideWith(this);
 public void collideWith(final Asteroid asteroid) {
 // Handle Spaceship-Asteroid collision.
 public void collideWith(final Spaceship spaceship) {
 // Handle Spaceship-Spaceship collision.
还可以在一层或两层上使用运行时codice_12检查。
C++.
截至目前，C++本身只支持单分派，下面是使用codice_13来模拟多分派的例子：
 // Example using run time type comparison via dynamic_cast
 struct Thing {
 virtual void collideWith(Thing&amp; other) = 0;
 struct Asteroid : Thing {
 void collideWith(Thing&amp; other) {
 // dynamic_cast to a pointer type returns NULL if the cast fails
 // (dynamic_cast to a reference type would throw an exception on failure)
 if (auto asteroid = dynamic_cast&lt;Asteroid*&gt;(&amp;other)) {
 // handle Asteroid-Asteroid collision
 } else if (auto spaceship = dynamic_cast&lt;Spaceship*&gt;(&amp;other)) {
 // handle Asteroid-Spaceship collision
 } else {
 // default collision handling here
 struct Spaceship : Thing {
 void collideWith(Thing&amp; other) {
 if (auto asteroid = dynamic_cast&lt;Asteroid*&gt;(&amp;other)) {
 // handle Spaceship-Asteroid collision
 } else if (auto spaceship = dynamic_cast&lt;Spaceship*&gt;(&amp;other)) {
 // handle Spaceship-Spaceship collision
 } else {
 // default collision handling here