Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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

Двойная отправка - это особый случай отправки нескольких сообщений.

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

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

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

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

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

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).
    }
}       

Ответ 2

Ну, ребята, код, размещенный Mark, не завершен, и что-то там не работает.

Так изменено и полно.

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

Ответ 4

В С# 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.