【问题标题】:Simplyfing code with a lot of ifs [closed]使用大量 if 简化代码 [关闭]
【发布时间】:2011-01-02 17:19:02
【问题描述】:

我有一个基于枚举的方法,首先要明确的是,我们有这种情况:

public void MyMetohd(Somestatus status)
{
if(status == Somestatus.Enum1)
{
DoA();
DoB();
DoC();
DoD();
DoE();
}
if(status == Somestatus.Enum2)
{
DoA();
DoB();
DoC();
DoD();
}

if(status == Somestatus.Enum3)
{
DoA();
DoB();
DoC();
}

if(status == Somestatus.Enum4)
{
DoA();
DoB();
}

if(status == Somestatus.Enum5)
{
DoA();
}
}

您将如何优化这种代码(它不是我的)?

【问题讨论】:

  • 检查是否不能重构代码以使用Strategy Pattern。 (也检查this)。您可以将其与工厂方法或工厂类结合使用。
  • 你说的是普通的普通枚举,还是一个标志?
  • 优化是什么意思?您的性能界限究竟是什么?目前是什么限制了它?
  • 不知道为什么没有人提到达夫的设备:en.wikipedia.org/wiki/Duff%27s_device

标签: c# optimization simplify


【解决方案1】:

通过优化,我假设您的意思是“制造 DRYer”。

您将不得不在易于阅读的代码(您所拥有的代码虽然有些重复)和尽可能少重复的代码之间取得平衡

只是输入这个让我觉得很脏,但如果你想要的是 DRY 和更少的 LOC,我认为它会满足你的需求。

switch (status)
            {
                case Somestatus.Enum1:
                    DoE();
                    goto SomeStatus.Enum2;
                case Somestatus.Enum2:
                    DoD();
                    goto SomeStatus.Enum3;
                case Somestatus.Enum3:
                    DoC();
                    goto SomeStatus.Enum4;
                case Somestatus.Enum4:
                    DoB();
                    goto SomeStatus.Enum5;
                case Somestatus.Enum5:
                    DoA();
                    break;
                default:
                    throw new InvalidArgumentException("Unknown Status");
            }

【讨论】:

    【解决方案2】:

    如果您设置enum 的每个成员的值,则可以使用比较。

    enum Somestatus : int
    {
        Enum1 = 1,
        Enum2 = 2,
        ...
    }
    

    然后只需使用比较来完成您的代码。因为你总是做DoA(),所以从那个开始。

    if(status <= Somestatus.Enum5)
        DoA();
    
    if(status <= Somestatus.Enum4)
        DoB();
    
    if(status <= Somestatus.Enum4)
        DoC();
    ...
    

    继续这样。这样,当值为 Enum1 时,将调用所有函数。

    【讨论】:

    • 很好的例子,我认为你的意思是这样,当值为 Enum1 时,将调用所有函数,因为 Enum1 是应该执行所有 5 个方法的唯一场景。
    • 我认为这样的“解决方案”很难阅读,也很难维护。
    • ++ 我会这样做。
    【解决方案3】:

    您可以在枚举值上使用字典键并使用 ActionAction&lt;T&gt; 的列表来执行。

    Dictionary<int,IList<Action>> actionsPerEnumValue;
    

    用枚举值和每个操作填充这个字典。

    在您的函数中获取每个值的函数列表并调用每个操作。

    foreach(var act in actionsPerEnumValue[status])
    {
        act();
    }
    

    请参阅this SO 答案以获取示例。

    【讨论】:

      【解决方案4】:

      乍一看switch 语句似乎是最好的方法,但即使在其中你也有很多重复的代码:

      switch (status)
      {
          case Somestatus.Enum1:
              DoA();
              DoB();
              DoC();
              DoD();
              DoE();
              break;
          case Somestatus.Enum2:
              DoA();
              DoB();
              DoC();
              DoD();
              break;
          ...
      }
      

      虽然这样更好,但仍然不理想,因为您重复调用 DoA 等。

      【讨论】:

      • 除非不中断。 (等等,C# 是否允许 case 语句不中断?)
      • 如果你省略了休息;它会延续到其他的地位价值吗?
      • @BoltClock - 不,你不能在 C# 中失败,你必须使用 goto
      • @BoltClock - 不,它没有:(
      • @Frederik - 如果您在案例之间有代码,则不会 - “与 C++ 不同,C# 不允许从一个切换部分继续执行到下一个切换部分。” msdn.microsoft.com/en-us/library/06tc147t(v=VS.100).aspx
      【解决方案5】:

      当你在相同的条件和不同的值下工作时,那个时候你最好使用 switch..case... 如果要测试多个条件,请使用 If..else。

      【讨论】:

        【解决方案6】:

        如果逻辑更简单,您也可以“反转”逻辑(取决于要覆盖的枚举数量与不同操作的数量):

        if(status == Somestatus.Enum1 || status == Somestatus.Enum2)
         DoA();
        
        if(status == Somestatus.Enum1 || status == Somestatus.Enum4)
         DoB();
        
        ...
        

        【讨论】:

          【解决方案7】:

          听起来你应该使用State Pattern

          【讨论】:

            【解决方案8】:

            已编辑:

            // ANOTHER WAY
            public void MyMetohd(Somestatus status)
            {
                switch(status)
                {
                    case Somestatus.Enum1:
                         do_("ABCDE");
                         break;
                    case Somestatus.Enum2:
                         do_("ABCD");
                         // and so on...
                    }
            }
            
            public static void do_(string s)
            {
                foreach(char ch in s)
                {
                    switch(ch)
                    {
                        case 'A':
                             doA();
                             break;
                        case 'B':
                             doB();
                             break;
                        case 'C':
                             doC();
                             break;
                        case 'D':
                             doD();
                             break;
                        case 'E':
                             doE();
                             break               
                    }
                }
            }
            

            【讨论】:

            • 你的第一种方式不编译,如前所述,c#需要显式跳转语句,不会落空。
            • @Brook:谢谢伙计!我现在永远不会忘记这一点:) +1
            • 没问题,我一开始也是这么想的,还有很多人。另一件事是您可以使用的跳转语句(break 或 goto)受到限制。如果他们允许您使用“继续”明确地失败,那就太好了。
            • 嗯...无论如何,我认为最好的办法是使用@Frederik Gheysels 建议的一些设计模式。
            【解决方案9】:

            使用模式来解决这个问题将是其他人建议的最佳解决方案。我想提供另一种解决方案。

            我强烈建议不要这样做。

            public void MyMetohd(Somestatus status)
                DoA();
                if (status != SomeStatus.Enum5) {
                    DoB();
                    if (status != SomeStatus.Enum4) {
                        DoC();
                        if (status != SomeStatus.Enum3) {
                            DoD();
                            if (status != SomeStatus.Enum2) {
                                DoE();
                            }
                        }
                    }
                }
            }
            

            【讨论】:

              【解决方案10】:

              虽然我自己会选择 Brook 的解决方案,但我想指出另一个优雅而简短的解决方案。

              public void MyMethod(Somestatus status)
              {
                  foreach (Action toDo in new Action[] { DoA, DoB, DoC, DoD, DoE }.Take(5 - (int)status))
                      toDo();
              }
              

              但是,这假设 Somestatus 是这样定义的:

              enum Somestatus
              {
                  Enum1,
                  Enum2,
                  Enum3,
                  Enum4,
                  Enum5
              }
              

              我喜欢这个解决方案作为一个学术,因为它很短,但它肯定没有很好的可读性或可维护性。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-11-23
                • 1970-01-01
                • 1970-01-01
                • 2018-12-30
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多