Двойная диспетчеризация в C#?
Я слышал/читал но не совсем понял что это значит.
когда я должен использовать эту технику и как бы я его использовать? Может кто-нибудь дать пример кода?
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
.