Вопрос

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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 Dispatcher

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top