【问题标题】:How to refactor the code which uses nested if-elses?如何重构使用嵌套 if-else 的代码?
【发布时间】:2019-05-03 06:16:25
【问题描述】:

我有一些用 C# 编写的代码,我需要为其添加一些条件。从我的一种方法中,我调用了另一种方法“UpdateData()”,它将一些数据更新到数据库中。现在为此,我需要添加一些条件。

  1. 配置文件中会有appsetting,是一个布尔值。如果值为true,则必须检查point2中的条件,如果值为false,则必须直接调用UpdateData()方法。
  2. 如果point1为真,则需要检查的类型和状态很少。

    条件 1:如果类型为“A”且状态为“完成” - 则调用“UpdateData()”。

    条件 2:如果类型为“B”且状态为“完成”或“部分”或“某些 xyz”,则调用“UpdateData()”。

为此,我编写了以下代码:

 bool checkStatus = Convert.ToBoolean(ConfigurationManager.AppSettings["CheckStatus"]);

if (checkStatus)
{
   if (type == "A" && status == "Complete")
   {
      UpdateData();
   }
   else if (type == "B" && (status == "Complete" || status == "Other status" || status == "someother status"))
   {
      UpdateData(); // for type B , data should not be updated if status is anything other than those in condition.
   }
}
else
{
   UpdateData();
}

这是按预期工作的。但我不想写嵌套的 if-else。如果明天我需要包含其他一些状态和类型,我需要在此处添加另一个 if 条件。

有没有更好或者优化的写法?

【问题讨论】:

  • 不管条件如何,都会经过UpdateData(),这是故意的吗?
  • @Steven,status 有很多值。只有当状态是代码中提到的两个或三个之一时才需要更新数据。
  • 如果您将这些条件拆分为方法,那么一种方法用于 A & 完成,一种方法用于 B & 完成,或其他状态,或其他状态,您可以制作 1 if 和 do if (cond1 () || cond(2) || !checkStatus) 更新数据

标签: c# nested-if


【解决方案1】:

我个人认为你所拥有的一切都很好。但是,如果您真的不喜欢它,我猜您可以使用本地方法

bool CheckA() => type == "A" && status == "Complete";
bool CheckB() => type == "B" && (status == "Complete" || status == "Other status" || status == "someother status")

if (!checkStatus || CheckA() || CheckB())
   UpdateData();

bool Check(string val, params string[] list) => type == val && list.Contains(status);

// uglay, yet approachable
if (!checkStatus ||
    Check("A", "Complete") ||
    Check("B", "Complete", "Other status", "someother status"))
{
   UpdateData();
}

注意:我个人不会这样做,尽管这可能是你的一杯茶

【讨论】:

  • 感谢您的回复。但我可以知道你为什么说你不会使用它。这不是标准的写法吗?
  • @CrazyCoder 我的意思是,我不会开始优化它,除非事情开始变得复杂并且不可读,即如果你必须检查状态列表等。你所拥有的是相当可读的
  • @好的明白了。我不想要嵌套的 if-else,因为这会使 SonarQube 设置的质量门失败。所以我试图避免任何嵌套的 if-else。但无论如何感谢您的建议。
  • @fubo 你让我挠头一秒:P
  • 我已经尝试使用您提到的第一种方法来使用本地函数。它给了我编译时错误:预期;或=。我读到它是 c# 6.0 中的一个功能。我正在使用VS2015。所以 c# 6.0 是其中的默认版本。还需要做什么才能使其发挥作用?
【解决方案2】:

如果是为了优化快速添加新的能力,我会把它放在一个数组中。

bool[] checks = new bool[]{
    (type == "A" && status == "Complete"),
    (type == "B" && (status == "Complete" || status == "Other status" || status == "someother status"))
}

if (!checkStatus || checks.Contains(false)) //'Contains' requires Linq, it also works with 'checks.Any(c => !c)'
{
    UpdateData();
}

这样您只需要在 bool 数组中添加一个新条件,而不必为它更新 if 语句。如果您不需要它,您可以快速注释掉它。
不过,它的用处取决于您可能希望添加多少条件。

【讨论】:

    【解决方案3】:

    我猜这段代码的主要目的是确保更新在满足条件时运行,这是通过单元测试完成的。

    所以,我会看看如何以可测试的方式编写这段代码,这样我就可以确定它会按照锡上所说的那样做。

    一种方法可能是这样的:

    public bool AppSettingsRunStatusMet(string appSettingsCheckStatus)
    {
        return Convert.ToBoolean(appSettingsCheckStatus);
    }
    
    public bool TypeARunStatusMet(string type, string status)
    {
       return (type.Equals("A")  && status.Equals("Complete"))
    }
    
    public bool TypeBRunStatusMet(string type, string status)
    {
       return (
           type.Equals("B") && 
           ( 
                status.Equals("Complete") || 
                status.Equals("Other status") || 
                status.Equals("someother status")
           )
    }
    

    这开始形成一个规则引擎,您可以根据需要拥有尽可能多的规则

    现在每个 if 语句都可以调用一个方法,并且每个规则都在自己的方法中分开,现在可以很容易地正确测试。

    您可以将所有规则放在它们自己的类中,让引擎返回一个状态,如果该状态为真,则运行更新。主要思想是将配置代码与状态分析以及运行更新并执行所需操作的代码分开。

    【讨论】:

      【解决方案4】:

      我更喜欢这样做,这样当出现新案例或验证标准发生变化时,我们就可以将新案例列入白名单。这很简单/可维护,适用于不断增长的有效案例集,几乎没有变化。

      using System;
      using System.Collections.Generic;
      
      public class Program
      {
            class Case{
              string type;
              string status;
               public Case(string type_val, string status_val ){
                    status = status_val;
                   type = type_val;
               }
      
              public override int GetHashCode()  
              {  
                  return type.GetHashCode()+status.GetHashCode();  
              }  
              public override bool Equals(object obj)  
              {  
                  Case test = obj as Case;  
                  if (test== null)  
                      return false;  
                  return type == test.type &&  status == test.status ;  
              } 
      
          }
          public static void Main()
          {
              bool checkStatus = true;
              HashSet<Case> vaild_cases = new HashSet<Case>();
              vaild_cases.Add(new Case("A","Complete"));
              vaild_cases.Add(new Case("B","Complete"));
              vaild_cases.Add(new Case("B","Other status"));
              vaild_cases.Add(new Case("B","someother status"));
              Case current_case = new Case("A","Complete");
              if (!checkStatus || vaild_cases.Contains(current_case))
                  UpdateData();
          }
      
          static void UpdateData()
          {
              Console.WriteLine("Hello, World!");
              return;
          }
      }  
      

      干杯!!

      【讨论】:

        【解决方案5】:

        另一种选择是:

        if((new string[] {"Complete", "Other status", "someother status"}.Contains(status) && type == "B") ||
           (status == "Complete" && type == "A") ||
           !checkStatus)
        {
            UpdateData();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多