Двойная диспетчеризация в C#?


Я слышал/читал но не совсем понял что это значит.

когда я должен использовать эту технику и как бы я его использовать? Может кто-нибудь дать пример кода?

4 59

4 ответа:

шаблон посетителя-это способ выполнения двойной отправки объектно-ориентированным способом.

это полезно, когда вы хотите выбрать, какой метод использовать для данного аргумента на основе его типа во время выполнения, а не во время компиляции.

двойная отправка является частным случаем множественная диспетчеризация.

когда вы вызываете виртуальный метод для объекта, это считается однократной отправкой, потому что фактический метод вызывается в зависимости от типа объекта один объект.

для двойной отправки учитывается как тип объекта, так и тип единственного аргумента метода. Это похоже на разрешение перегрузки метода, за исключением того, что тип аргумента определяется во время выполнения в режиме двойной отправки, а не статически во время компиляции.

в multiple-dispatch метод может иметь несколько аргументов, переданных ему, и какая реализация используется, зависит от типа каждого аргумента. Порядок, в котором оцениваются типы, зависит от язык. В LISP он проверяет каждый тип от первого до последнего.

языки с несколькими отправками используют универсальные функции, которые являются просто делкарациями функций и не похожи на универсальные методы, которые используют параметры типа.

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

using System.Linq;  

class DoubleDispatch
{ 
    public T Foo<T>(object arg)
    { 
        var method = from m in GetType().GetMethods()
                   where    m.Name == "Foo" 
                         && m.GetParameters().Length==1
                         && arg.GetType().IsAssignableFrom
                                           (m.GetParameters()[0].GetType())
                         && m.ReturnType == typeof(T)
                   select m;

        return (T) method.Single().Invoke(this,new object[]{arg});          
    }

    public int Foo(int arg) { /* ... */ }

    static void Test() 
    { 
        object x = 5;
        Foo<int>(x); //should call Foo(int) via Foo<T>(object).
    }
}       

ну эй, ребята, код, отправленный Марком, не завершен, и то, что когда-либо есть, не работает.

Так подправлено и завершено.

class DoubleDispatch
{
    public T Foo<T>(object arg)
    {
        var method = from m in GetType().GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic)
                     where m.Name == "Foo"
                           && m.GetParameters().Length == 1
                           //&& arg.GetType().IsAssignableFrom
                           //                  (m.GetParameters()[0].GetType())
                           &&Type.GetType(m.GetParameters()[0].ParameterType.FullName).IsAssignableFrom(arg.GetType())
                           && m.ReturnType == typeof(T)
                     select m;


        return (T)method.Single().Invoke(this, new object[] { arg });
    }

    public int Foo(int arg)
    {
        return 10;
    }

    public string Foo(string arg)
    {
        return 5.ToString();
    }

    public static void Main(string[] args)
    {
        object x = 5;
        DoubleDispatch dispatch = new DoubleDispatch();

        Console.WriteLine(dispatch.Foo<int>(x));


        Console.WriteLine(dispatch.Foo<string>(x.ToString()));

        Console.ReadLine();
    }
}

спасибо Марк и другие за хорошее объяснение на двойной шаблон диспетчера

Double-dispatch-это другое название для шаблон Visitor.

У меня есть статья, которую я писал несколько лет назад об использовании отражения для реализации шаблона Visitor. http://www.agileprogrammer.com/dotnetguy/articles/ReflectionVisitor.aspx

C# 4 вводит псевдо тип dynamic разрешающего вызова функции во время выполнения (а не компиляции). (То есть используется тип времени выполнения выражения). Двойная (или мульти-отправка) может быть упрощена до:

class C { }

static void Foo(C x) => Console.WriteLine(nameof(Foo));
static void Foo(object x) => Console.WriteLine(nameof(Object));

public static void Main(string[] args)
{
    object x = new C();

    Foo((dynamic)x); // prints: "Foo"
    Foo(x);          // prints: "Object"
}

будьте осторожны с целыми типами. Так как dynamic трактуется как System.Object Он никогда не будет называть void Foo(int x) в примере выше.

Отметим также, что с помощью dynamic вы запрещаете компилятору статические анализаторы для проверки этой части кодекса. Вы должны тщательно рассмотреть использование dynamic.