Что такое - одиночная и множественная отправка (по отношению к.NET)?

Это так же, как перегрузка, если нет, не могли бы вы предоставить и пример каждого в C#

Я прочитал ответы на аналогичный вопрос, заданный в SO ... Я не понял ответы, отправленные на него.

Подобный вопрос задан здесь

РЕДАКТИРОВАТЬ: С новым "динамическим" ключевым словом в C# 4.0 ... это сделало бы язык "multi dispatch" включен?

9 ответов

Решение

C# использует одну отправку, которая включает перегруженные методы. Когда у вас есть код

stringBuilder.Append(parameter);

диспетчер просматривает все методы, определенные в классе stringBuilder, и находит правильный.

Для примера с несколькими рассылками, давайте посмотрим на Пролог (который я впервые смог придумать). Вы можете определить функцию в прологе так:

func(Arg1, Arg2) :- ....body....

Это не определено внутри какого-либо класса, но в глобальной области видимости. Затем вы можете позвонить func(Arg1, Arg2) на любые два аргумента и эта функция будет вызвана. Если вы хотите что-то вроде перегрузки, вы должны проверить типы аргументов внутри функции и определить это несколько раз:

func(Arg1, Arg2) :- is_number(Arg1), is_string(Arg2), ....body....
func(Arg1, Arg2) :- is_string(Arg1), is_list(Arg2), ....body....
func(Arg1, Arg2) :- is_number(Arg1), is_list(Arg2), ....body....

Затем будут проверены любые два типа аргументов, которые вы отправите, - это часть множественной отправки.

Короче говоря, одиночная отправка просматривает только методы, определенные в первом аргументе (в нашем первом примере, stringBuilder), а затем разрешает правильную перегрузку для вызова с использованием других аргументов. Множественная диспетчеризация имеет методы / функции, определенные в глобальной области, и обрабатывает все аргументы одинаково во время разрешения перегрузки.

Надеюсь, я дал понять, что это довольно сложный вопрос.


Обновление: я забыл упомянуть, многократная отправка происходит во время выполнения, в то время как одиночная отправка происходит во время компиляции.
Обновление № 2: По-видимому, это было не так.

Многократная отправка - это "форма" перегрузки...

Например, C# является единственной диспетчеризацией, потому что, если выясняется, какой метод вызывать, основываясь только на одном аргументе, указатель this. Когда у вас есть что-то вроде этого:

Base base= new Derived();
base.DoSomething();

метод Derived.DoSomething вызывается, даже если вы вызывали его через базовый указатель. Теперь, если у нас есть следующее:

class Derived : Base
{
  public override void Process(Stream stream);
  public override void Process(FileStream stream);
  public override void Process(MemoryStream stream);
}

И мы делаем это:

Stream stream= new MemoryStream(...);
Base b= new Derived();
b.Process(stream);

Затем мы будем вызывать метод Process(Stream) из Derived поскольку C# делает единственную диспетчеризацию на указателе объекта (b) и затем использует информацию времени компиляции, чтобы решить, какой метод вызвать. Даже если поток является MemoryStream, одна система диспетчеризации будет игнорировать это.

В мультидиспетчерской системе будет рассмотрен указатель объекта (как в C#) И будут рассмотрены типы аргументов во время выполнения. В приведенном выше примере, поскольку поток на самом деле является MemoryStream, система будет вызывать метод Process(MemoryStream).

Multi-диспетчеризация связана с перегрузкой методов, но не одинакова. Первый - динамическое решение во время выполнения, второй - статическое решение во время компиляции. Существует неявное преимущество в гибкости, но, следовательно, затраты на многопотоковую передачу.

AFAIK язык может поддерживать любой, но не оба, хотя оба могут быть смоделированы (мультидиспетчер может быть смоделирован с посетителями). C# определяет типы данных во время компиляции и, следовательно, не является мультидиспетчерским языком, поэтому примеры не возможны.

(предостережение: я не на 100% в этом)


addendum: на самом деле в Википедии есть статья на эту тему, которая кажется довольно тщательной, да и полезный пример LISP

Одиночная / множественная диспетчеризация являются своего рода перегрузкой во время выполнения. Одиночная отправка более известна как виртуальные функции. Точная функция, вызываемая при вызове виртуальной функции, определяется на основе типа объекта во время выполнения. Двойная диспетчеризация - это то же самое, что распространяется на работу с двумя объектами - обычно this параметр и второй параметр. Вы можете реализовать это без особых сложностей, используя Vistor Pattern. Многократная диспетчеризация еще больше расширяет эту концепцию до большего количества параметров, но ее значительно сложнее реализовать в таких языках, как C# (не то, что это невозможно сделать, это просто сложно). Некоторые языки реализуют эту способность прямо из коробки.

например, в.NET функция ToString() является примером одиночной отправки

// Single dispatch
Object o = GetSomeObject(); // Return SomeType casted to Object.
o.ToString(); // Call SomeType::ToString instead of just Object::ToString

// Double dispatch (this version won't work in C#)
Shape s1 = GetSquare();
Shape s2 = GetCircle();
s1.Intersects(s2); // If C# supported double dispatch, this would call Square::Intersects(Circle) not Square::Intersects(Shape)

В ОО томится текст:

SomeType b;
a = b.Foo(c, d)

Означает, что объекту b передается сообщение Foo с параметрами c и d. Одиночная отправка означает, что только ОДИН аспект этой системы отвечает за определение во время выполнения, какой (из многих возможных) методов Foo будет фактически вызван.

Модель java, C# и большинство других языков ОО использует то, что только тип времени выполнения "b" может "решить", каков фактический вызов метода. Таким образом, если у вас есть две реализации SomeType, каждая из которых обеспечивает различную реализацию Foo, тогда решение о том, какой использовать, основывается исключительно на том, какой тип b окажется в этой точке. Если существует множество перегрузок Foo, то решение о том, какую перегрузку использовать, является решением времени компиляции, основанным исключительно на типах времени компиляции b, c и d.

Тогда это одна отправка, единственная точка выбора - система типов, связанная с b.

Множественная диспетчеризация позволила бы во время выполнения позволить типам времени выполнения b, c и d решать, какой метод вызывать (такое решение неизбежно является более сложным).

В более динамичной системе, где концепция четко определенных типов является более гибкой (скажем, система, основанная на прототипах, а не на модели наследования, которую вы знаете из C++/java/C#), тогда решение о том, какой метод вызывать, будет полностью принято к фактическим случаям b, c и d.

В C# 4.0 мультиметоды включены с новым динамическим ключевым словом:

используя Систему; namespace Example {class Wheel {public void RepairWhell () {}}

class Chassis
{
    public void RepairChassis() { }
}

class Engine
{
    public void RepairEngine() { }
}

class CarWorkshop
{
    public string Repair(Wheel value)
    {
        value.RepairWhell();
        return "wheel repaired";
    }
    public string Repair(Chassis value)
    {
        value.RepairChassis();
        return "chassis repaired";
    }
    public string Repair(Engine value)
    {
        value.RepairEngine();
        return "engine repaired";
    }
}

class Program
{
    static void Main(string[] args)
    {
        dynamic carWorkshop = new CarWorkshop();

        var whell = new Wheel();
        var chassis = new Chassis();
        var engine = new Engine();

        Console.WriteLine(carWorkshop.Repair(whell));
        Console.WriteLine(carWorkshop.Repair(chassis));
        Console.WriteLine(carWorkshop.Repair(engine));
        Console.ReadLine();
    }
}

}

Динамическое внедрение мультиметодов, очень мощная парадигма, в C#.

Вы можете использовать динамическое ключевое слово для реализации мульти-диспетчеризации в C#.

interface IA { }
interface IB { }
class CA1 : IA {}
class CA2 : IA {}
class CA11 : CA1 {}
class CB1 : IB {}
class CB2 : IB {}

class MD
{
    public enum X { X } ;
    public static void Foo(IA a, IB b, X dispatch = X.X) { Foo((dynamic)a, (dynamic)b); }
    static void Foo(IA a, IB b) { Console.WriteLine("IA IB"); }
    static void Foo(CA1 a, CB1 b) { Console.WriteLine("CA1 CB1"); }
    static void Foo(CA2 a, CB1 b) { Console.WriteLine("CA2 CB1"); }
    static void Foo(CA1 a, CB2 b) { Console.WriteLine("CA1 CB2"); }
    static void Foo(CA2 a, CB2 b) { Console.WriteLine("CA2 CB2"); }
    static void Foo(CA11 a, CB2 b) { Console.WriteLine("CA11 CB2"); }
}
class Program
{
    static void Main(string[] args)
    {
        var a1 = new CA1();
        var a11 = new CA11();
        var a2 = new CA2();
        var b1 = new CB1();
        var b2 = new CB2();
        MD.Foo(a1, b1);
        MD.Foo(a2, b1);
        MD.Foo(a1, b2);
        MD.Foo(a2, b2);
        MD.Foo(a11, b1);
        MD.Foo(a11, b2);
    }
}

Разрешение Foo((динамическое)a,(динамическое)b)) выполняется во время выполнения и выбирает один из перегруженных методов Foo на основе конкретных типов "a" и "b".

Извините, пример, который я привожу, пока не ошибся. Это не правильная версия:

class Wheel
{
    public void RepairWhell() { }
}

class Chassis
{
    public void RepairChassis() { }
}

class Engine
{
    public void RepairEngine() { }
}

class CarWorkshop
{
    public string Repair(Wheel value)
    {
        value.RepairWhell();
        return "wheel repaired";
    }
    public string Repair(Chassis value)
    {
        value.RepairChassis();
        return "chassis repaired";
    }
    public string Repair(Engine value)
    {
        value.RepairEngine();
        return "engine repaired";
    }
}

class Program
{
    static void Main(string[] args)
    {
        var carWorkshop = new CarWorkshop();

        dynamic whell = new Wheel();
        dynamic chassis = new Chassis();
        dynamic engine = new Engine();

        Console.WriteLine(carWorkshop.Repair(whell));
        Console.WriteLine(carWorkshop.Repair(chassis));
        Console.WriteLine(carWorkshop.Repair(engine));
        Console.ReadLine();
    }
}

Так что ответ это да. C# обеспечивает несколько отправок.

#include <iostream>

class Pet {
};

class Cat: public Pet {
};

class Dog: public Pet {
};

class Human {
};

class Man : public Human {
        public:
                void Kick(Cat& victim);
                void Kick(Dog& victim);
};

class Woman : public Human {
        public:
                void Kick(Cat& victim);
                void Kick(Dog& victim);
};

void Man::Kick(Cat& victim) {
        std::cout << "Meow!!!" << std::endl;
}

void Woman::Kick(Cat& victim) {
        std::cout << "I won't kick a cat" << std::endl;
}

void Man::Kick(Dog& victim) {
        std::cout << "I won't kick a dog" << std::endl;
}

void Woman::Kick(Dog& victim) {
        std::cout << "Woof!!!" << std::endl;
}

int main(int argc, char** argv) {
        Man kicker;
        Dog victim;
        Pet zoo[] = { victim };
        kicker.Kick(victim);
//      kicker.Kick(zoo[0]);   // No multimethods
        return 0;
}

На данный момент C++ не может выяснить во время выполнения Pet на самом деле Cat или Dog,

Если бы был какой-то способ сделать это во время выполнения (чтобы вышеприведенный код компилировался с закомментированной строкой), было бы сказано, что C++ поддерживает множественную диспетчеризацию или мультиметоды.

Другие вопросы по тегам