Reputation: 9245
I'm trying to iterate a list of items. The items are all part of a common interface. They are as described in this question. I want to use a foreach
loop to go through them, but execute different actions depending on what type it is.
For simplicity's sake, let's say the actions I want to execute are as follows:
ProcessLine(MachineLine ML); //For MachineLines
ProcessLine(MachineCircle MC); //For MachineCircles
How can this iteration be accomplished to account for the multiple data types?
Upvotes: 2
Views: 4036
Reputation: 5874
you can either use the keyword dynamic , if you are working in .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
}
}
Upvotes: 0
Reputation: 26588
Jeff's solution is the first choice, alternatively, if it's too much for you to change now, you can also use function overloading as well.
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)
{
....
}
Upvotes: 0
Reputation: 134611
The best way to handle this IMHO is to have the types inherit from a common base class/interface which has a method that does the action you want. Then call the common method within the loop. In your case, I would make it a base class instead since these are is-a relationships and add the ProcessLine()
method to the base class.
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();
}
Let polymorphism do the work for you.
Upvotes: 3
Reputation: 113472
I would seriously consider if this is the most appropriate design in this context. Are you sure the IMachine
interface shouldn't have a Process
method? Each machine could implement this as appropriate, and then the loop just becomes:
foreach (IMachine machine in machines)
{
machine.Process();
}
Anyway, to answer the question as asked, here's one way to do it. The idea is to keep trying a "speculative cast" to a target-type until it succeeds or we are out of options. This is normally done with the as
operator, followed by a 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(...);
}
}
As you can see, this pattern is ugly. For a cleaner solution, you might also want to take a look at C# - Is there a better alternative than this to 'switch on type' if there are a large number of implementers.
Upvotes: 6
Reputation: 724462
Assuming you have defined the proper overloads for ProcessLine()
, you simply test the types of these objects during each iteration, and then cast accordingly and call the method. Something like this:
foreach (IMachine m in machineList) {
if (m is MachineLine) {
ProcessLine((MachineLine) m);
} else if (m is MachineCircle) {
ProcessLine((MachineCircle) m);
}
}
To improve your program design, you may wish to consider the other suggestions here (add a Process()
method to your interface, etc).
Upvotes: 2
Reputation: 6159
This also sounds like a nice candidate for the Visitor design pattern...
http://en.wikipedia.org/wiki/Visitor_pattern
Upvotes: 0
Reputation: 8885
You should change your ProcessLine method to accept an IMachine instead, and make it call different methods depending on the type. It will make the code clearer, and you might reuse the logic somewhere else later on. Like this:
foreach (IMachine m in machineList)
ProcessLine(m);
The code in ProcessLine would look like:
void ProcessLine(IMachine machine)
{
if (machine is MachineLine)
ProcessMachineLine(MachineLine)
else if (machine is MachineCircle)
ProcessMachineCircle(MachineCircle)
}
Upvotes: 0
Reputation: 1464
foreach (var m in list){
if (m is MachineLine) ProcessLine((MachineLine) m);
else if (m is MachineCircle) ProcessLine((MachineCircle) m);
}
Upvotes: 1
Reputation: 29427
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 );
}
}
Upvotes: 1