Итерирование списка разных типов данных? - PullRequest
2 голосов
/ 06 декабря 2010

Я пытаюсь перебрать список предметов. Все элементы являются частью общего интерфейса. Они такие же, как описано в этом вопросе . Я хочу использовать цикл foreach для их прохождения, но выполнять разные действия в зависимости от типа.

Для простоты, скажем, следующие действия, которые я хочу выполнить:

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

Как выполнить эту итерацию для учета нескольких типов данных?

Ответы [ 9 ]

5 голосов
/ 06 декабря 2010

Я бы серьезно подумал, является ли это наиболее подходящим дизайном в этом контексте. Вы уверены, что в интерфейсе IMachine не должно быть метода Process? Каждая машина может реализовать это соответствующим образом, и тогда цикл просто становится:

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

В любом случае, чтобы ответить на вопрос в том виде, в котором он был задан, вот один из способов сделать это. Идея состоит в том, чтобы продолжать пробовать «умозрительное приведение» к целевому типу, пока это не удастся или у нас не будет выбора. Обычно это делается с помощью оператора as, за которым следует 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(...);
    }
}

Как видите, эта модель ужасна. Для более чистого решения вы также можете взглянуть на C # - есть ли лучшая альтернатива, чем эта, чтобы «включить тип» , если имеется большое количество разработчиков.

3 голосов
/ 06 декабря 2010

Лучший способ справиться с этим IMHO - это иметь типы, наследуемые от общего базового класса / интерфейса, у которого есть метод, который выполняет желаемое действие.Затем вызовите общий метод в цикле.В вашем случае я бы вместо этого сделал его базовым классом, поскольку это отношения is-a, и добавил бы метод ProcessLine() к базовому классу.

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();
}

Пусть полиморфизм сделает всю работу за вас.

2 голосов
/ 06 декабря 2010

Предполагая, что вы определили правильные перегрузки для ProcessLine(), вы просто проверяете типы этих объектов во время каждой итерации, а затем приводите их соответствующим образом и вызываете метод.Примерно так:

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

Чтобы улучшить дизайн вашей программы, вы можете рассмотреть другие предложения здесь (добавить метод Process() в ваш интерфейс и т. Д.).

1 голос
/ 06 декабря 2010
foreach (var m in list){
  if (m is MachineLine) ProcessLine((MachineLine) m);
  else if (m is MachineCircle) ProcessLine((MachineCircle) m);
}
1 голос
/ 06 декабря 2010
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 голосов
/ 06 декабря 2010

вы можете использовать ключевое слово dynamic, если вы работаете в .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
}

}
0 голосов
/ 06 декабря 2010

Решение Джеффа является первым выбором, в качестве альтернативы, если вы слишком много хотите изменить сейчас, вы также можете использовать перегрузку функций.

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 голосов
/ 06 декабря 2010

Это также звучит как хороший кандидат на шаблон проектирования Visitor ...

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

0 голосов
/ 06 декабря 2010

Вам следует изменить метод ProcessLine, чтобы вместо него принимать IMachine, и заставить его вызывать разные методы в зависимости от типа. Это сделает код более понятным, и вы можете позже использовать логику где-нибудь еще. Как это:

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

Код в ProcessLine будет выглядеть так:

void ProcessLine(IMachine machine)
{
   if (machine is MachineLine)
        ProcessMachineLine(MachineLine)
   else if (machine is MachineCircle)
        ProcessMachineCircle(MachineCircle)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...