Pergunta

Já ouvi/li o termo, mas não entendo muito bem o que significa.

Quando devo usar essa técnica e como usá-la?Alguém pode fornecer um bom exemplo de código?

Foi útil?

Solução

O padrão de visitante é uma forma de fazer despacho duplo de forma orientada a objetos.

É útil quando você deseja escolher qual método usar para um determinado argumento com base em seu tipo em tempo de execução, em vez de em tempo de compilação.

O duplo envio é um caso especial de expedição múltipla.

Quando você chama um método virtual em um objeto, isso é considerado despacho único porque o método real chamado depende do tipo do objeto único.

Para despacho duplo, são levados em consideração tanto o tipo do objeto quanto o tipo do único argumento do método.Isto é como a resolução de sobrecarga de método, exceto que o tipo de argumento é determinado em tempo de execução em despacho duplo, em vez de estaticamente em tempo de compilação.

No despacho múltiplo, um método pode ter vários argumentos passados ​​para ele e qual implementação é usada depende do tipo de cada argumento.A ordem em que os tipos são avaliados depende do idioma.No LISP, ele verifica cada tipo do primeiro ao último.

Linguagens com despacho múltiplo fazem uso de funções genéricas, que são apenas declarações de funções e não são como métodos genéricos, que usam parâmetros de tipo.

Para fazer envio duplo em C#, você pode declarar um método com um único argumento de objeto e depois métodos específicos com 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).
    }
}       

Outras dicas

Bem, pessoal, o código postado por Mark não está completo e tudo o que existe não está funcionando.

Tão ajustado e 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();
    }
}

Obrigado Mark e outros pela boa explicação sobre o padrão Double Dispatcher

C# 4 apresenta o pseudo tipo dynamic que resolve a chamada de função em tempo de execução (em vez de tempo de compilação).(Ou seja, o tipo de tempo de execução da expressão é usado).O despacho duplo (ou múltiplo) pode ser simplificado para:

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"
}

Observe também usando dynamic você evita que o analisador estático do compilador examine esta parte do código.Você deve, portanto, considerar cuidadosamente o uso de dynamic.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top