Двойная отправка в C #? - PullRequest
       35

Двойная отправка в C #?

61 голосов
/ 04 сентября 2008

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

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

Ответы [ 3 ]

55 голосов
/ 04 сентября 2008

Шаблон посетителя - это способ выполнить двойную диспетчеризацию объектно-ориентированным способом.

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

Двойная отправка - это особый случай многократная отправка .

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

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

В режиме множественной диспетчеризации методу может быть передано несколько аргументов, и какая реализация используется, зависит от типа каждого аргумента. Порядок оценки типов зависит от языка. В 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).
    }
}       
11 голосов
/ 08 июня 2011

Что ж, ребята, код, размещенный Марком, не завершен, и все, что там есть, не работает.

Настроен и завершен.

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 Dispatcher

2 голосов
/ 12 июля 2017

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 вы не позволяете статическому анализатору компилятора исследовать эту часть кода. Поэтому вы должны внимательно рассмотреть вопрос об использовании dynamic.

...