example - ¿Despacho doble en C#?
summary example c# (4)
He escuchado / leído el término pero no entiendo muy bien lo que significa.
¿Cuándo debería usar esta técnica y cómo la usaría? ¿Alguien puede proporcionar una buena muestra de código?
Bueno, amigos, el código publicado por Mark no está completo y lo que no está funcionando.
Tan ajustado y completo.
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();
}
}
Gracias Mark y otros por una buena explicación sobre el patrón Double Dispatcher
C # 4 introduce la dynamic
pseudo tipo que resuelve la llamada de función en tiempo de ejecución (en lugar de tiempo de compilación). (Es decir, se usa el tipo de tiempo de ejecución de la expresión). Double- (o multi-dispatch) se puede simplificar a:
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"
}
Sin embargo, ten cuidado con los tipos integrales. Como dynamic
se trata como System.Object
, nunca llamará a void Foo(int x)
en el ejemplo anterior.
Tenga en cuenta también que al usar dynamic
evita que el compilador analizadores estáticos examinen esta parte del código. Debes considerar cuidadosamente el uso dynamic
.
Double-dispatch es otro nombre para el patrón Visitor .
Tengo un artículo que escribí hace unos años sobre el uso de Reflection para implementar el patrón Visitor. http://www.agileprogrammer.com/dotnetguy/articles/ReflectionVisitor.aspx
El patrón de visitante es una forma de hacer doble envío de una manera orientada a objetos.
Es útil para cuando quiere elegir qué método usar para un argumento dado en función de su tipo en tiempo de ejecución en lugar de tiempo de compilación.
El envío doble es un caso especial de despacho múltiple .
Cuando llama a un método virtual en un objeto, eso se considera envío único porque el método real que se llama depende del tipo del único objeto.
Para el envío doble, se tienen en cuenta tanto el tipo del objeto como el tipo de argumento único del método. Esto es como la resolución de sobrecarga del método, excepto que el tipo de argumento se determina en tiempo de ejecución en doble despacho en lugar de estáticamente en tiempo de compilación.
En el despacho múltiple, un método puede tener múltiples argumentos pasados y la implementación que se usa depende del tipo de cada argumento. El orden en que se evalúan los tipos depende del idioma. En LISP, comprueba cada tipo de principio a fin.
Los idiomas con despacho múltiple hacen uso de funciones genéricas, que son solo desviaciones de funciones y no son como métodos genéricos, que usan parámetros de tipo.
Para hacer un doble despacho en C # , puede declarar un método con un único argumento de objeto y luego métodos específicos con tipos específicos:
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).
}
}