2010-12-05 59 views
2

J'essaie de parcourir une liste d'éléments. Les éléments font tous partie d'une interface commune. Ils sont tels que décrits dans this question. Je veux utiliser une boucle foreach pour les parcourir, mais exécuter des actions différentes selon le type.Itérer une liste de différents types de données?

Par souci de simplicité, disons que les actions que je veux exécuter sont les suivantes:

ProcessLine(MachineLine ML); //For MachineLines 
ProcessLine(MachineCircle MC); //For MachineCircles 

Comment cette itération être accompli pour tenir compte des multiples types de données?

Répondre

2

En supposant que vous avez défini les surcharges correctes pour ProcessLine(), vous testez simplement les types de ces objets à chaque itération, puis vous les castez en conséquence et appelez la méthode. Quelque chose comme ceci:

foreach (IMachine m in machineList) { 
    if (m is MachineLine) { 
     ProcessLine((MachineLine) m); 
    } else if (m is MachineCircle) { 
     ProcessLine((MachineCircle) m); 
    } 
} 

Pour améliorer la conception de votre programme, vous voudrez peut-être examiner les autres suggestions ici (ajouter une méthode Process() à votre interface, etc.).

+0

wow! presque le même code :) – Lorenzo

+0

@Lorenzo: J'ai aussi écrit 'instanceof' au début mais c'est en fait' is'. Peut-être que vous voulez corriger votre code aussi :) – BoltClock

+0

+1 vous avez absolument raison! ;) – Lorenzo

1
List<IMachine> m = new List<IMachine>(); 
foreach (IMachine machine in m) { 
    if (m is MachineLine) { 
     ProcessLine(m as MachineLine); 
    } 
    else if (m is MachineCircle) { 
     ProcessLine(m as MachineCircle); 
    } 
} 
+0

Je changerais la distribution de 'm as MachineLine' à '(MachineLine) m'. Vous avez déjà vérifié le type de l'objet, et l'utilisation de 'as' ne fait que répéter cette vérification. – TheEvilPenguin

+0

IMO il y a aussi 2 opérations de cast en utilisant '(MachineLine) m' – Lorenzo

+1

Les deux * sont * et * comme * font une opération de transtypage. Pour faire seulement 1 cast, vous devez utiliser * as *, mais gardez le résultat dans une variable, et au lieu d'utiliser * is *, comparez la variable à * null *. – Ran

4

je serais sérieusement considérer si cela est la conception la plus appropriée dans ce contexte. Êtes-vous sûr que l'interface IMachine ne devrait pas avoir une méthode Process? Chaque machine pourrait mettre en œuvre ce, le cas échéant, et la boucle devient juste:

foreach (IMachine machine in machines) 
{ 
    machine.Process(); 
} 

Quoi qu'il en soit, pour répondre à la question posée, voici une façon de le faire. L'idée est de continuer à essayer un "casting spéculatif" à un type de cible jusqu'à ce qu'il réussisse ou que nous ne soyons plus à court d'options. Ceci est normalement effectué avec l'opérateur as, suivi d'un null-test.

IList<IMachine> machines = ... 

foreach (IMachine machine in machines) 
{ 
    MachineLine machineLine = machine as MachineLine; 

    if (machineLine != null) 
     ProcessLine(machineLine); 

    else 
    { 
     MachineCircle machineCircle = machine as MachineCircle; 

     if (machineCircle != null) 
      ProcessCircle(machineCircle); 

     else throw new UnknownMachineException(...); 
    } 
} 

Comme vous pouvez le voir, ce modèle est moche. Pour une solution plus propre, vous pouvez également jeter un oeil à C# - Is there a better alternative than this to 'switch on type' s'il y a un grand nombre d'implémenteurs.

+1

+1 pour suggérer une méthode 'IMachine.Process()' dans votre premier paragraphe. – BoltClock

1
foreach (var m in list){ 
    if (m is MachineLine) ProcessLine((MachineLine) m); 
    else if (m is MachineCircle) ProcessLine((MachineCircle) m); 
} 
0

Vous devez modifier votre méthode ProcessLine pour accepter un IMachine à la place, et lui faire appeler différentes méthodes en fonction du type. Cela rendra le code plus clair et vous pourrez réutiliser la logique quelque part plus tard. Comme ceci:

foreach (IMachine m in machineList) 
     ProcessLine(m); 

Le code ProcessLine ressemblerait à ceci:

void ProcessLine(IMachine machine) 
{ 
    if (machine is MachineLine) 
     ProcessMachineLine(MachineLine) 
    else if (machine is MachineCircle) 
     ProcessMachineCircle(MachineCircle) 
} 
2

Le La meilleure façon de gérer cette IMHO est d'avoir les types hérités d'une classe/interface de base commune qui a une méthode qui fait l'action que vous voulez. Ensuite, appelez la méthode commune dans la boucle. Dans votre cas, je voudrais en faire une classe de base car ce sont des relations is-a et ajouter la méthode ProcessLine() à la classe de base.

public abstract class MachineShape 
{ 
    public abstract void ProcessLine(); 
} 

public class MachineLine : MachineShape 
{ 
    public override void ProcessLine() 
    { 
     // implement for MachineLine 
    } 

    public double X1; 
    public double Y1; 
    public double X2; 
    public double Y2; 
    public double Thickness; 
} 

public class MachineCircle : MachineShape 
{ 
    public override void ProcessLine() 
    { 
     // implement for MachineCircle 
    } 

    public double CenterX; 
    public double CenterY; 
    public double Radius; 
} 

MachineShape[] shapes = ...; 
foreach (var shape in shapes) 
{ 
    shape.ProcessLine(); 
} 

Laissez le polymorphisme faire le travail pour vous.

0

La solution de Jeff est le premier choix, sinon, si c'est trop pour vous de changer maintenant, vous pouvez également utiliser la surcharge de fonction.

foreach (IMachine m in machineList){ 
     //sorry I can't test it since I don't have visual studio installed. 
     //but you get the idea 
     ProcessLine((m.getType())m); 
} 

function ProcessLine(MachineLine m) 
{ 
... 
} 

function ProcessLine(MachineCircle m) 
{ 
.... 
} 
0

vous pouvez utiliser le mot-clé dynamique, si vous travaillez dans .net 4,0

foreach (dynamic m in machineList) { 

if(m.GetType()==typeof(MachineLine)) 
{ 
    // code goes here 
} 
else if(m.GetType()==typeof(MachineCircle)) 
{ 
    // code goes here 
} 

}