2017-04-07 4 views
0

J'ai un arbre d'héritage de classes Line différentes, commençant par le Line -classe. Je veux être capable de croiser chaque ligne avec chaque autre ligne, et parfois, je ne connais aucun des types d'exécution, par ex. J'appelle Line.Intersect(Line) (donc j'ai besoin de double expédition). Cela appellera toujours la surcharge la plus abstraite de la méthode Intersect surchargée, par ex. Circle.Intersect(Line) au lieu de Circle.Intersect(actualType). Voici quelques exemples de code:Envoi double à l'intérieur d'un seul arbre d'héritage

class Program 
{ 
    static void Main(string[] args) 
    { 
    Line straightLine = new StraightLine(); 
    Line circle = new Circle(); 

    // Will print: "Circle intersecting a line." 
    // But should print: "Circle intersecting a straight line." 
    circle.Intersect(straightLine); 

    Console.ReadLine(); 
    } 
} 


abstract class Line 
{ 
    public abstract void Intersect(Line line); 

    public abstract void Intersect(StraightLine straightLine); 

    public abstract void Intersect(Circle circle); 
} 


class StraightLine : Line 
{ 
    public override void Intersect(Line line) 
    { 
    Console.WriteLine("Straigth line intersecting a line."); 
    } 

    public override void Intersect(StraightLine straightLine) 
    { 
    Console.WriteLine("Straight line intersecting a straight line."); 
    } 

    public override void Intersect(Circle circle) 
    { 
    Console.WriteLine("Straight line intersecting a circle."); 
    } 
} 


class Circle : Line 
{ 
    public override void Intersect(Line line) 
    { 
    Console.WriteLine("Circle intersecting a line."); 
    } 

    public override void Intersect(Circle circle) 
    { 
    Console.WriteLine("Circle intersecting a circle."); 
    } 

    public override void Intersect(StraightLine straightLine) 
    { 
    Console.WriteLine("Circle intersecting a straight line."); 
    } 
} 

Une solution possible est d'utiliser dynamic, que je fais actuellement. Cependant, je souhaite migrer vers une bibliothèque .NET Standard, où dynamic n'est pas autorisé.

Existe-t-il d'autres façons de faire ce travail? Je serais prêt à changer la classe abstraite pour une ou plusieurs interfaces, si cela aide. Peut-être que le modèle de visiteur est applicable, bien que je l'ai seulement vu utilisé pour différents arbres d'héritage (et le trouve assez moche).

Répondre

0

Il est possible d'émuler la double répartition en utilisant la réflexion. Ciblant .NET Standard 1.1 et utilisant le paquet Nuget System.Reflection, la méthode Intersect(Line line) n'a pas besoin d'être abstraite ou virtuelle, mais seulement d'être implémentée une seule fois.

Voici le code entier exemple pour la bibliothèque standard .NET (je retourne maintenant un string au lieu d'utiliser Console.WriteLine(), puisque celui-ci ne sont pas disponibles dans la norme .NET):

using System.Reflection; 

namespace IntersectLibrary 
{ 
    public abstract class Line 
    { 
    public string Intersect(Line line) 
    { 
     var method = this.GetType().GetRuntimeMethod(nameof(Intersect), new[] { line.GetType() }); 
     return (string)method.Invoke(this, new[] { line }); 
    } 

    public abstract string Intersect(StraightLine straightLine); 

    public abstract string Intersect(Circle circle); 
    } 


    public class StraightLine : Circle 
    { 
    public override string Intersect(StraightLine straightLine) 
    { 
     return "Straight line intersecting a straight line."; 
    } 

    public override string Intersect(Circle circle) 
    { 
     return "Straight line intersecting a circle."; 
    } 
    } 


    public class Circle : Line 
    { 
    public override string Intersect(Circle circle) 
    { 
     return "Circle intersecting a circle."; 
    } 

    public override string Intersect(StraightLine straightLine) 
    { 
     return "Circle intersecting a straight line."; 
    } 
    } 
} 

S'il vous plaît noter que lorsque Le ciblage .NET Framework, System.Reflection, propose différentes méthodes et le code devra être modifié.

Dans une application de la console, ce qui se passera:

using System; 
using IntersectLibrary; 

namespace ConsoleApplication 
{ 
    class Program 
    { 
    static void Main(string[] args) 
    { 
     Line straightLine = new StraightLine(); 
     Line circle = new Circle(); 
     Circle circle2 = new Circle(); 

     // Calls "Line.Intersect(Line)", and correctly 
     // prints "Circle intersecting a straight line.". 
     Console.WriteLine(circle.Intersect(straightLine)); 

     // Also calls "Line.Intersect(Line)", 
     // since the argument's compile-time type is "Line". 
     Console.WriteLine(circle2.Intersect(straightLine)); 

     // Calls "Line.Intersect(Circle)", 
     // since the argument's compile-time type is "Circle". 
     // At runtime, the call will be resolved to 
     // "StraightLine.Intersect(Circle)" via single dispatch. 
     Console.WriteLine(straightLine.Intersect(circle2)); 

     Console.ReadLine(); 
    } 
    } 
} 

Si vous avez maintenant un objet de type compilation Line et l'un d'un type concret (par exemple Circle), il est préférable d'appeler Line.Intersect(Circle), puisque cela n'aura pas besoin de la réflexion (plus lente) pour résoudre l'appel de la méthode. Cependant, Circle.Intersect(Line) fonctionnera également, et le plus important, appeler Line.Intersect(Line) est maintenant toujours possible.