多态与多重分派有什么区别?

时间:2020-03-06 14:38:19  来源:igfitidea点击:

...或者他们是同一回事吗?我注意到每个人都有自己的Wikipedia条目:多态性,多次派发,但是我很难看到概念之间的差异。

编辑:过载如何适合所有这些?

解决方案

多重调度是一种多态性。在Java / C#/ C ++中,通过继承和重写实现了多态,但这不是基于两个或者多个参数的多重调度(与Java / C#/ C ++一样,不只是this)。

多次分派更类似于函数重载(如Java / C ++中所示),所调用的函数取决于参数的运行时类型,而不是其静态类型。

多个分派依赖于基于多态性。在C ++,C#,VB.NET等中遇到的典型多态性使用单调度-i.e.被调用的函数仅取决于单个类实例。多个调度依赖于多个类实例。

我之前从未听说过Multiple Dispatch,但是在浏览Wikipedia页面后,看起来很像MD与方法的参数一起使用时是一种多态性。

从本质上讲,多态是一个概念,可以将对象视为其基础的任何类型。因此,如果我们有"汽车"和"卡车",它们都可以看作是"车辆"。这意味着我们可以为任何一个调用任何" Vehicle"方法。

多重分派看起来很相似,因为它使我们可以使用带有多个类型的参数的方法来调用方法,但是我在说明中看不到某些要求。首先,它似乎不需要通用的基本类型(不是我可以想象的在没有void *的情况下实现THAT),并且可以包含多个对象。

因此,我们可以调用其他地方定义的" StartObject(Object C)"方法并对其进行编码以在运行时检查参数类型,而不是对列表中的每个对象调用" Start()"方法(这是经典的多态示例)。时间并妥善处理。此处的区别在于,必须在类中内置Start()方法,而StartObject()方法可以在类外部定义,因此各种对象不需要符合接口。

如果需要使用不同的参数调用Start()方法,则可能会很好。也许是Car.Start(Key carKey)vs.Missile.Start(int launchCode)

但是两者都可以称为StartObject(theCar)或者StartObject(theMissile)

有趣的概念...

如果要在概念上等效于方法调用

(obj_1, obj_2, ..., obj_n)->method

依赖于元组中的每种特定类型,那么我们需要多次调度。多态性对应于n = 1的情况,并且是OOP的必要特征。

使用多个分派,一个方法可以有多个参数传递给它,并且使用哪种实现取决于每个参数的类型。评估类型的顺序取决于语言。在LISP中,它从头到尾检查每种类型。
具有多个分派的语言使用泛型函数,这些泛型函数只是函数声明,与使用类型参数的泛型方法不同。

多重分派允许方法调用的参数的子类型多态性。

单调度还允许一种更有限的多态(对于实现相同接口或者继承相同基类的对象使用相同的方法名称)。这是多态性的经典示例,其中方法在子类中被覆盖。

除此之外,泛型提供了参数类型多态性(即,相同的泛型接口可用于不同的类型,即使它们之间没有类似List <T>的关联:它可以是任何类型的列表,并且使用相同的方式而不管)。

多态是一种工具,它允许语言/程序在运行时根据发送给该方法的参数类型来决定要调用的方法。

语言/运行时使用的参数数量决定了语言支持的多态性的"类型"。

单调度是一种多态性,其中仅使用一个参数(消息" this"或者" self"的接收者)来确定呼叫。

多重调度是一种多态性,其中多个参数用于确定要调用的方法。在这种情况下,接收方以及方法参数的类型用于告知要调用的方法。

因此,我们可以说多态是通用术语,而多次和单次调度是多态的特定类型。

附录:重载发生在编译时。它使用编译期间可用的类型信息来确定要调用的方法类型。单次/多次分派在运行时发生。

样例代码:

using NUnit.Framework;

namespace SanityCheck.UnitTests.StackOverflow
{
    [TestFixture]
    public class DispatchTypes
    {
        [Test]
        public void Polymorphism()
        {
            Baz baz = new Baz();
            Foo foo = new Foo();

            // overloading - parameter type is known during compile time
            Assert.AreEqual("zap object", baz.Zap("hello"));
            Assert.AreEqual("zap foo", baz.Zap(foo));

            // virtual call - single dispatch. Baz is used.
            Zapper zapper = baz;
            Assert.AreEqual("zap object", zapper.Zap("hello"));
            Assert.AreEqual("zap foo", zapper.Zap(foo));

            // C# has doesn't support multiple dispatch so it doesn't
            // know that oFoo is actually of type Foo.
            //
            // In languages with multiple dispatch, the type of oFoo will 
            // also be used in runtime so Baz.Zap(Foo) will be called
            // instead of Baz.Zap(object)
            object oFoo = foo;
            Assert.AreEqual("zap object", zapper.Zap(oFoo));
        }

        public class Zapper
        {
            public virtual string Zap(object o) { return "generic zapper" ; }
            public virtual string Zap(Foo f) { return "generic zapper"; }
        }

        public class Baz : Zapper
        {
            public override string Zap(object o) { return "zap object"; }
            public override string Zap(Foo f) { return "zap foo"; }
        }

        public class Foo { }
    }
}