【问题标题】:Why is the cylcomatic complexity of this function 12?为什么这个函数的圈复杂度是 12?
【发布时间】:2015-04-23 20:38:59
【问题描述】:

我有一个 (C#) 函数,它检查四组条件并返回一个布尔值。如果其中任何一个为真,则返回true。我确信我可以简化逻辑,但我希望它具有相当的可读性。

Visual Studios 中的 CodeMaid 扩展,告诉我函数的圈复杂度是 12。我 looked it up 圈复杂度是

通过源代码的独立路径数

我不明白为什么它是 12。我可以想到两种方式,圈复杂度应该是 2,因为它总是经过相同的路径,但可以返回 truefalse。或者可以理解它是否是 16,因为最后在一起的四个布尔值 or 可以分别为真或假,2*2*2*2 = 16。

谁能告诉我为什么是 12?甚至可以显示一个图表,以便我可以可视化不同的路径?

public bool FitsCheckBoxCriteria(TaskClass tasks)
{
    // note: bool == true/false comparisons mean you don't have to cast 'bool?' as bool


    // if neither checkboxes are checked, show everything
    bool showEverything = NoShutDownRequiredCheckBox.IsChecked == false &&
                          ActiveRequiredCheckBox.IsChecked == false; 

    // if both are checked, only show active non-shutdown tasks
    bool showActiveNonShutdown = ActiveRequiredCheckBox.IsChecked == true &&
                                 tasks.Active == "YES" &&
                                 NoShutDownRequiredCheckBox.IsChecked == true &&
                                 tasks.ShutdownRequired == "NO";

    // if active is checked but shudown isn't, display all active
    bool showActive = ActiveRequiredCheckBox.IsChecked == true &&
                      tasks.Active == "YES" &&
                      NoShutDownRequiredCheckBox.IsChecked == false;

    // if non-shutdown is checked but active isn't, display all non-shutdown tasks
    bool showNonShutdown = NoShutDownRequiredCheckBox.IsChecked == true &&
                           tasks.ShutdownRequired == "NO" &&
                           ActiveRequiredCheckBox.IsChecked == false;

    return showEverything || showActiveNonShutdown || showActive || showNonShutdown;
}

提前致谢。

编辑:

我把它改成了这个。为复选框条件分配局部变量没有任何效果,但是从“YES”/“NO”创建布尔值将复杂度提高到 14,我想我理解。

public bool FitsCheckBoxCriteria(LubeTask tasks)
{
    bool noShutdownReqChecked = (bool)NoShutDownRequiredCheckBox.IsChecked;
    bool activeChecked = (bool)ActiveRequiredCheckBox.IsChecked;

    bool active = tasks.Active == "YES" ? true : false;
    bool shutdownReq = tasks.ShutdownRequired == "YES" ? true : false;

    // if neither checkboxes are checked, show everything
    bool showEverything = !noShutdownReqChecked && !activeChecked;

    // if both are checked, only show activeChecked non-shutdown tasks
    bool showActiveNonShutdown = activeChecked && noShutdownReqChecked && active && !shutdownReq;

    // if activeChecked is checked but shudown isn't, display all activeChecked
    bool showActive = activeChecked && !noShutdownReqChecked && active;

    // if non-shutdown is chceked but activeChecked isn't, display all non-shutdown tasks
    bool showNonShutdown = noShutdownReqChecked && !activeChecked && !shutdownReq;

    return showEverything || showActiveNonShutdown || showActive || showNonShutdown;
}

【问题讨论】:

  • 只是好奇,如果删除所有== true 语句并将== false 更改为!,复杂性会发生什么?它们是多余的
  • @moarboilerplate 我知道它们是多余的。只是这些类型是bool? 而不是bool,所以为了便于阅读,我没有将它们全部转换,而是进行了比较。 msdn.microsoft.com/en-us/library/bb384091.aspx
  • 是的,但它对复杂性有什么影响?另外,如果您有兴趣对此进行优化,我会使用标志枚举和按位运算来为每个位设置权限。
  • (虽然这确实消除了布尔值的可空方面)
  • 嗯,我摆脱了比较并做了演员表,但并没有改变复杂性。不过那会很整洁。

标签: c# complexity-theory cyclomatic-complexity


【解决方案1】:

关键在于“独立路径”。

我将重写你的代码以缩短它,以便我们讨论它。

public bool FitsCheckBoxCriteria(TaskClass tasks)
{
    bool E1 = A1 && A2; 

    bool E2 = B1 && B2 && B3 && B4;

    bool E3 = C1 && C2 && C3;

    bool E4 = D1 && D2 && D3;

    return E1 || E2 || E3 || E4;
}

圈复杂度是独立路径的数量。这不是可能的返回值总数 (2)。

&& 运算符和 ||算子是短路操作;如果 A1 为假,不评估 A2。同样,如果 E1 为真,则不计算 E2。

如果将所有 &&s 替换为 & 并将所有 ||s 替换为 |在上面的代码中,圈复杂度为 1,因为通过代码只有一条路径。 (但这不会使它成为更好的代码)。

事实上,有 72 种可能的路径...

  1. A1、B1、C1、D1、E1被评估;其他人不是。
  2. A1,A2,B1,C1,D1,E1被评估;其他人不是。
  3. 评估A1、B1、B2、C1、D1、E1;其他人不是。
  4. 评估 A1、A2、B1、B2、C1、D1、E1;其他人不是。 等等……

但路径 4 不包含之前路径中尚未包含的任何新代码。这就是“独立路径”的定义——每条路径都必须包含新代码。

所以在这个例子中,你可以手动计算代码如下:

1 + 此代码中的短路运算符数 (11) = 12。

Wikipedia has an excellent in depth explanation.

【讨论】:

    【解决方案2】:

    这只是一个猜测,但我认为每个赋值都是 +2(如果 =true/else =false),然后在 return 语句中为每个可能的退出条件 +1。所以它可能会放松为:

    bool showEverything = false;
    if (...) { showEverything = true; } +1
    else { showEverything = false; } +1
    
    bool showActiveNonShutdown = ... +2 if/else
    bool showActive = ... +2 if/else
    bool showNonShutdown = ... +2 if/else
    
    if (showEverything) {...} +1
    else if (showActiveNonShutdown) {...} +1
    else if (showActive) {...} +1
    else if (showNonShutdown) {...} +1
    else {false}
    

    【讨论】:

    • 这是一种非常有趣的观察方式!
    【解决方案3】:

    C# 使用短路评估,这意味着如果存在x && y 表达式,则仅在必要时评估y,更准确地说,如果x 为真。这意味着result = x && y; 有两个独立的执行路径:(1) 如果x 为假,则仅评估x 并且result 获得假值(不评估y)但(2)如果x为真y 也被评估,result 得到y 的评估结果。这意味着每个 &&|| 运算符都会增加圈复杂度,在第一个示例中,有 8 个 && 和 3 个 ||算子,所以该方法的圈复杂度为 12。

    【讨论】:

      猜你喜欢
      • 2010-10-29
      • 2017-08-18
      • 2012-10-29
      • 1970-01-01
      • 2013-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多