Comment puis-je stocker différents objects dans une seule liste

J’ai deux classes une classe d’arc et une classe de ligne

public class Arc { protected double startx; protected double starty; protected double endx; protected double endy; protected double radius; public Arc(){} } public class Line { protected double startx; protected double starty; protected double endx; protected double endy; protected double length; public Line(){} } 

Mais je veux stocker des arcs et des lignes dans la même liste, alors j’ai essayé une interface comme celle-ci

 public interface Entity { double StartX(); double StratY(); double EndX(); double EndY(); } 

Ensuite, j’ai ajouté les méthodes appropriées à chaque classe et ajouté le code permettant d’utiliser l’interface. Maintenant, je peux append les deux types d’objects à une liste, mais je veux obtenir la longueur d’un object de ligne et je ne veux pas append de méthode de longueur à l’arc ou à l’interface. Ma seule option est-elle de convertir l’object de ligne en un object de ligne comme celui-ci?

 List entities = new List(); entities.Add(new Line(10,10,5,5)); Line myLine = (Line)Entities[0] double length = myLine.Length(); 

* En supposant que j’ai toutes les méthodes appropriées dans la classe de ligne.
Ou y a-t-il une manière meilleure / différente de faire cela?

Ou y a-t-il une manière meilleure / différente de faire cela?

Si vos objects descendent d’une classe commune, vous pouvez les stocker dans la même collection. Afin de faire quelque chose d’utile avec vos objects sans jeter la sécurité de type, vous devez implémenter le modèle de visiteur :

 public interface EntityVisitor { void Visit(Arc arc); void Visit(Line line); } public abstract class Entity { public abstract void Accept(EntityVisitor visitor); } public class Arc : Entity { protected double startx; protected double starty; protected double endx; protected double endy; protected double radius; public override void Accept(EntityVisitor visitor) { visitor.Visit(this); } } public class Line : Entity { protected double startx; protected double starty; protected double endx; protected double endy; protected double length; public override void Accept(EntityVisitor visitor) { visitor.Visit(this); } } 

Une fois que cela est en place, vous créez une instance de EntityVisitor chaque fois que vous devez faire quelque chose d’utile avec votre liste:

 class EntityTypeCounter : EntityVisitor { public int TotalLines { get; private set; } public int TotalArcs { get; private set; } #region EntityVisitor Members public void Visit(Arc arc) { TotalArcs++; } public void Visit(Line line) { TotalLines++; } #endregion } class Program { static void Main(ssortingng[] args) { Entity[] entities = new Entity[] { new Arc(), new Line(), new Arc(), new Arc(), new Line() }; EntityTypeCounter counter = entities.Aggregate( new EntityTypeCounter(), (acc, item) => { item.Accept(acc); return acc; }); Console.WriteLine("TotalLines: {0}", counter.TotalLines); Console.WriteLine("TotalArcs: {0}", counter.TotalArcs); } } 

Et pour ce que cela vaut, si vous êtes ouvert à l’essai de nouvelles langues, les unions marquées + correspondance de F # sont une alternative pratique au modèle de visiteur .

Si vous êtes dans .NET 3.5 ou supérieur, vous pouvez le rendre un peu moins laid de cette façon:

 List entities = new List(); // add some lines and some arcs var lines = entities.OfType(); 

Ensuite, il vous suffit de parcourir les lines , qui contiendront toutes les lignes (fortement typées en tant que lignes) et rien d’autre.

Je ne dis pas que c’est la meilleure approche; Je dis seulement que c’est une façon de faire ce que vous faites. Je suis d’accord avec Shmoopty que c’est un problème d’architecture.

Puisque Arc et Line partagent des données (startx et quelques autres champs), je vous suggère d’utiliser une classe abstraite commune en tant que classe parent plutôt qu’une interface. Par exemple, Figure .

Le casting est correct, même si je recommanderais plutôt:

 Line myLine = Entities[0] as Line; 

Il renverra null si Entities[0] ne peut pas être converti en Line , plutôt que de lancer une exception. Vous pourrez vérifier si myLine est nul par la suite.

Oui, c’est le seul moyen, compte tenu de vos contraintes.

Je suggérerais d’append de la longueur à l’interface (car arc a une longueur).

La formule peut être trouvée ici .

Sinon, vous pouvez append la méthode à l’interface et lui faire renvoyer une exception NotImplementedException.

Demandez à l’interface d’implémenter une propriété “Size” (ou appelez-la “magnitue” ou “Range”…)

Cela correspond au rayon de l’arc et à la longueur des lignes.

Ensuite, vous pouvez obtenir Entity.Size.

Cela dépend de la façon dont vous voulez traiter les Arcs lorsque vous les retirez de la liste. Si vous essayez de convertir un arc en ligne, vous obtiendrez une erreur d’exécution. Par conséquent, vous devez tout d’abord vérifier si l’entité avec laquelle vous travaillez est une ligne.

Une façon de gérer les arcs consiste à utiliser le modèle d’object nul. Il peut être judicieux d’append à Arc une méthode de longueur qui renvoie 0. Ainsi, le code qui extrait les objects de la liste n’a plus à se soucier de leur type.