Domanda

Ho sentito/letto il termine ma non capisco bene cosa significhi.

Quando dovrei usare questa tecnica e come la userei?Qualcuno può fornire un buon esempio di codice?

È stato utile?

Soluzione

Il modello visitatore è un modo per eseguire il doppio invio in modo orientato agli oggetti.

È utile quando si desidera scegliere quale metodo utilizzare per un determinato argomento in base al tipo in fase di esecuzione anziché in fase di compilazione.

Il doppio invio è un caso speciale invio multiplo.

Quando chiami un metodo virtuale su un oggetto, viene considerato invio singolo perché il metodo effettivo chiamato dipende dal tipo del singolo oggetto.

Per il doppio invio, vengono presi in considerazione sia il tipo dell'oggetto che il tipo dell'unico argomento del metodo.È simile alla risoluzione dell'overload del metodo, con la differenza che il tipo di argomento viene determinato in fase di esecuzione in modalità double-dispatch anziché staticamente in fase di compilazione.

Nell'invio multiplo, a un metodo possono essere passati più argomenti e l'implementazione utilizzata dipende dal tipo di ciascun argomento.L'ordine in cui i tipi vengono valutati dipende dalla lingua.In LISP controlla ogni tipo dal primo all'ultimo.

I linguaggi con invio multiplo fanno uso di funzioni generiche, che sono solo dichiarazioni di funzioni e non sono come metodi generici, che utilizzano parametri di tipo.

Per eseguire il doppio invio in C#, puoi dichiarare un metodo con un unico argomento oggetto e quindi metodi specifici con tipi specifici:

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

Altri suggerimenti

Bene, ciao ragazzi, il codice pubblicato da Mark non è completo e qualunque cosa ci sia non funziona.

Quindi ottimizzato 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();
    }
}

Grazie Mark e altri per la bella spiegazione sul modello Double Dispatcher

C# 4 introduce lo pseudotipo dynamic che risolve la chiamata di funzione in fase di esecuzione (invece che in fase di compilazione).(Ovvero, viene utilizzato il tipo runtime dell'espressione).L'invio doppio (o multiplo) può essere semplificato in:

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

Nota anche usando dynamic impedisci all'analizzatore statico del compilatore di esaminare questa parte del codice.Dovresti quindi considerare attentamente l'uso di dynamic.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top