Adam S
Adam S

Reputation: 9245

Iterating a list of different data types?

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

Answers (9)

wizzardz
wizzardz

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

James Lin
James Lin

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

Jeff Mercado
Jeff Mercado

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

Ani
Ani

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

BoltClock
BoltClock

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

Ran
Ran

Reputation: 6159

This also sounds like a nice candidate for the Visitor design pattern...

http://en.wikipedia.org/wiki/Visitor_pattern

Upvotes: 0

Can Gencer
Can Gencer

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

PeterT
PeterT

Reputation: 1464

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

Upvotes: 1

Lorenzo
Lorenzo

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

Related Questions