【问题标题】:Iterating a list of different data types?迭代不同数据类型的列表?
【发布时间】:2011-05-20 16:40:49
【问题描述】:

我正在尝试迭代项目列表。这些项目都是公共界面的一部分。它们如this question 中所述。我想使用foreach 循环来遍历它们,但根据它的类型执行不同的操作。

为简单起见,假设我要执行的操作如下:

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

如何完成此迭代以考虑多种数据类型?

【问题讨论】:

    标签: c# list foreach


    【解决方案1】:

    我会认真考虑这是否是在这种情况下最合适的设计。你确定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# - Is there a better alternative than this to 'switch on type'

    【讨论】:

    • +1 用于在您的第一段中建议 IMachine.Process() 方法。
    【解决方案2】:

    处理这个恕我直言的最佳方法是让类型继承自一个公共基类/接口,该基类/接口具有执行您想要的操作的方法。然后在循环内调用常用方法。在您的情况下,我会将其设为基类,因为这些是关系并将 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();
    }
    

    让多态为你完成这项工作。

    【讨论】:

      【解决方案3】:

      假设您已经为ProcessLine() 定义了正确的重载,您只需在每次迭代期间测试这些对象的类型,然后进行相应的转换并调用该方法。像这样的:

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

      为了改进您的程序设计,您不妨考虑此处的其他建议(向您的界面添加Process() 方法等)。

      【讨论】:

      • @Lorenzo:我一开始也写了instanceof,但实际上是is。可能也想修复你的代码:)
      • 当我进入我的时,似乎有大约3个instanceof;曾考虑发表评论,但我想我只是添加另一个答案......现在看来相当多余。
      • @PeterT:是的,instanceof/is 一直都在吸引我。
      • 我不得不提醒自己is最近又回到了c#,今天在java中写instanceof,所以我现在脑子里还挺新鲜的。
      • 虽然很简洁,但使用这种技术,每种候选类型实际上都有 2 个强制转换操作。
      【解决方案4】:
      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 );
          }
      }
      

      【讨论】:

      • 我会将演员阵容从“m as MachineLine”更改为“(MachineLine)m”。您已经检查了对象的类型,使用“as”只是重复该检查。
      • IMO 也有 2 个使用 (MachineLine)m 的演员操作
      • isas 都进行强制转换操作。为了只进行 1 次强制转换,您必须使用 as,但将结果保存在变量中,而不是使用 is,而是将变量与 null 进行比较.
      【解决方案5】:
      foreach (var m in list){
        if (m is MachineLine) ProcessLine((MachineLine) m);
        else if (m is MachineCircle) ProcessLine((MachineCircle) m);
      }
      

      【讨论】:

        【解决方案6】:

        您应该将 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)
        }
        

        【讨论】:

          【解决方案7】:

          这听起来也是访问者设计模式的一个不错的候选...

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

          【讨论】:

            【解决方案8】:

            Jeff 的解决方案是首选,或者如果你现在改变太多,你也可以使用函数重载。

            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)
            {
            ....
            } 
            

            【讨论】:

              【解决方案9】:

              如果您在 .net 4.0 中工作,您可以使用关键字 dynamic

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

              【讨论】:

                猜你喜欢
                • 2017-09-02
                • 2016-03-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-12-06
                • 1970-01-01
                相关资源
                最近更新 更多