2008-09-03 9 views
57

J'ai entendu/lu le terme mais je ne comprends pas très bien ce que cela signifie.Envoi double en C#?

Quand dois-je utiliser cette technique et comment l'utiliser? Quelqu'un peut-il fournir un bon échantillon de code?

+0

Actuellement, c'est la meilleure façon de le faire: https://blogs.msdn.microsoft.com/curth/2008/11/15/c-dynamic-and-multiple-dispatch/ –

+0

Grand exemple de double expédition à [Application de la composition d'objet pour construire des modèles de domaine riche] (https://vimeo.com/195774910). – jsuddsjr

+0

[Méfiez-vous de la double expédition] (https://lostechies.com/derekgreer/2010/04/19/double-dispatch-is-a-code-smell/). Vous pouvez probablement l'éviter pour un meilleur entretien du code. –

Répondre

54

Le modèle de visiteur est une manière de faire un double envoi de manière orientée objet.

Cela est utile lorsque vous souhaitez choisir la méthode à utiliser pour un argument donné en fonction de son type lors de l'exécution plutôt que de la compilation.

La double répartition est un cas spécial de envoi multiple.

Lorsque vous appelez une méthode virtuelle sur un objet, cela est considéré comme à répartition unique car la méthode actuelle est appelée en fonction du type de l'objet unique.

Pour la répartition double, le type de l'objet et le type de l'argument unique méthode sont pris en compte. C'est comme la résolution de surcharge de méthode, sauf que le type d'argument est déterminé lors de l'exécution en double-dispatch au lieu de statiquement au moment de la compilation.

Dans la distribution multiple, plusieurs arguments peuvent être transmis à une méthode et l'implémentation utilisée dépend du type de chaque argument. L'ordre d'évaluation des types dépend de la langue. Dans LISP, il vérifie chaque type du premier au dernier.

Les langages à répartition multiple utilisent des fonctions génériques, qui ne sont que des suppressions de fonctions et ne sont pas comme des méthodes génériques, qui utilisent des paramètres de type.

Pour faire double expédition en C#, vous pouvez déclarer une méthode avec un seul argument objet et méthodes spécifiques avec des types spécifiques:

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

Utilisation merveilleuse de LINQ avec réflexion - Je n'avais pas pensé à l'appliquer à ces objets fastidieux de xxxInfo auparavant. Merci! –

+24

Je ne suis pas si sûr que ce soit une bonne idée. Cela n'implémente pas du tout la double distribution, j'ai besoin de savoir * à compile-t * quel est le type de la chose afin de spécifier le paramètre type !! Vous pouvez aussi ne pas vous embêter avec tout le code de réflexion et simplement jeter l'objet. Est-ce que je manque quelque chose? – ljs

+4

Il implémente la double distribution en ce qu'il distribue à la fois le type d'exécution de l'objet (dérivé de DoubleDispatch) et l'argument de la méthode. La réflexion sur le type de retour est utilisée pour étendre le machanisme aux sous-classes, donc vous pouvez ajouter "string Foo (string)" à une sous-classe et ça marchera. –

11

Eh bien hey les gars, le code affiché par Mark isnt complète et quoi que ce soit ne fonctionne pas.

Tellement ajusté et complet.

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

Merci Mark et d'autres pour belle explication sur le modèle Double Dispatcher

+0

Je suis d'accord. Ce code est complet et montre ce qu'est réellement la double répartition. L'explication de la réponse de Mark et ce code vont parfaitement ensemble. –

+0

N'écrirait pas "Console.WriteLine (dispatch.Foo (x));" être un moyen plus dynamique et utile de profiter du motif? –

0

C# 4 présente le type pseudo dynamic qui résout l'appel de fonction lors de l'exécution (au lieu de compilation). (C'est-à-dire que le type d'exécution de l'expression est utilisé). Double- (ou multi-dispatch) peut être simplifié à:

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

Soyez prudent cependant avec les types intégraux. Puisque dynamic est traité comme System.Object, il n'appellera jamais void Foo(int x) dans l'exemple ci-dessus.

Notez également qu'en utilisant dynamic vous empêchez le compilateur d'un analyseur statique d'examiner cette partie du code. Vous devriez examiner attentivement l'utilisation dynamic.