【问题标题】:Full lists of expression for C# code which +1 for Cyclomatic ComplexityC# 代码表达式的完整列表,其中 +1 表示循环复杂度
【发布时间】:2011-12-05 00:05:06
【问题描述】:

我需要为我的 C# 项目中的每个方法构建一个控制流程图(带有节点和边的简单流程图),以演示计算圈复杂度的图形方式。

我首先使用 VS 2010 计算了圈复杂度,然后我构建了图表以确保结果值与从 VS 计算的值相同。但是,我在这里遇到了一些问题,因为我不确定哪个表达式实际上被认为是圈复杂度的 +1。

我们来看一个例子:

 public ActionResult Edit(string id, string value)
    {
        string elementId = id;
        // Use to get first 4 characters of the id to indicate which category the element belongs
        string fieldToEdit = elementId.Substring(0, 4);

        // Take everything AFTER the 1st 4 characters, this will be the ID
        int idToEdit = Convert.ToInt32(elementId.Remove(0, 4));

        // The value to be return is simply a string:
        string newValue = value;

        var food = dbEntities.FOODs.Single(i => i.FoodID == idToEdit);

        // Use switch to perform different action according to different field
        switch (fieldToEdit)
        {
            case "name": food.FoodName = newValue; break;
            case "amnt": food.FoodAmount = Convert.ToInt32(newValue); break;
            case "unit": food.FoodUnitID = Convert.ToInt32(newValue); break;
            // ** DateTime format need to be modified in both view and plugin script
            case "sdat": food.StorageDate = Convert.ToDateTime(newValue); break;
            case "edat": food.ExpiryDate = Convert.ToDateTime(newValue); break;
            case "type": food.FoodTypeID = Convert.ToInt32(newValue); break;

            default: throw new Exception("invalid fieldToEdit passed");

        }
        dbEntities.SaveChanges();
        return Content(newValue);
    }

对于这种方法,VS计算的圈复杂度为10。但是,只有7个case语句,我不明白其他表达式对复杂度有什么影响。

我搜索了许多来源,但无法获得将被计算在内的所有表达式的完整列表。

有人可以帮忙吗?或者有什么工具可以从 C# 代码生成控制流程图?

提前谢谢你...

【问题讨论】:

  • 我对此没有答案,但我的猜测是除了 switch 分支之外,VS 还会计算可能引发异常的情况。例如,如果 elementId == null 方法将抛出 NullReferenceException 试图执行第二行。这可以被认为是一个单独的执行路径。

标签: c# visual-studio-2010 cyclomatic-complexity


【解决方案1】:

您应该首先尝试使用图表来可视化圈复杂度。在浏览您的代码时,我设法计算出 10。为了更好地理解这一点,请查看以下内容:

public void MyMethod()
{
    Console.WriteLine("Hello ShennyL");
}

这有1的圈复杂度,因为这里只有一个可能的路径,那就是显示消息。

public void AnotherMethod()
{
    if (someCondition)
    {
        Console.WriteLine("Hello Shennly");
    }
}

这次我们的圈复杂度为 2。+1 被添加到 if、while、for、foreach。在这种情况下,有两条路径。如果 someCondition 为 true,将显示消息(第一个可能的路径),如果 someCondition 为 false,则不会显示消息(第二个可能的路径)。

如果您查看 Windows 窗体中 Dispose 的实现,它看起来像这样:

protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}

这里的圈复杂度为 3。在 && 的情况下,两个值都必须为真才能计算内部的表达式。这意味着如果 both disposing 为真 并且 (components != null) 为真,则您拥有第一条路径。如果disposing 为假,则您有第二条路径。第三条路径来自components 可以为空,因此它的评估结果为假。因此,您的圈复杂度为三。

如果是switch,您将获得+1,对于出现在其中的每个case(和default),您将获得+1。对于您的方法,您有六个case 语句和一个default 加上switch,总共有8 个。

正如我在开始时所说,如果您尝试根据图形来可视化您的代码,它可以像这样分解(我正在将我的 cmets 添加到您的代码中并删除您的 cmets):

public ActionResult Edit(string id, string value)                   
{                   
    string elementId = id; // First path, cyclomatic complexity is 1

    string fieldToEdit = elementId.Substring(0, 4); // Same path, CC still 1  

    int idToEdit = Convert.ToInt32(elementId.Remove(0, 4)); // Same path, CC still 1

    string newValue = value; // Same path, CC still 1

    var food = dbEntities.FOODs.Single(i => i.FoodID == idToEdit); // Boolean expression inside your lambda. The result can go either way, so CC is 2.

    switch (fieldToEdit) // Switch found, so CC is 3
    {                   
        case "name": food.FoodName = newValue; break; // First case - CC is 4
        case "amnt": food.FoodAmount = Convert.ToInt32(newValue); break; // Second case - CC is 5
        case "unit": food.FoodUnitID = Convert.ToInt32(newValue); break; // Third case - CC is 6
        case "sdat": food.StorageDate = Convert.ToDateTime(newValue); break; // Fourth case - CC is 7
        case "edat": food.ExpiryDate = Convert.ToDateTime(newValue); break; // Fifth case - CC is 8
        case "type": food.FoodTypeID = Convert.ToInt32(newValue); break; // Sixth case - CC is 9

        default: throw new Exception("invalid fieldToEdit passed"); // Defaul found - CC is 10

    }                   
    dbEntities.SaveChanges(); // This belongs to the first path, so CC is not incremented here.                   
    return Content(newValue);                   
}        

我可能有几点是错误的,但基本上这是计算圈复杂度背后的想法。您还应该了解,在某些情况下您无法减少此值(如果您需要使用 switch/case,则 CC 会增加)。此外,如果你的变量有糟糕的命名(就像你试图混淆你的代码)圈复杂度可能会返回较低的值,因为它无法理解你的命名是糟糕的。命名会增加您的复杂性,如果您不在代码中使用 cmets,6 个月后您将很难弄清楚为什么圈复杂度为 3,但您无法理解正在编写的内容。

【讨论】:

    猜你喜欢
    • 2011-12-24
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-23
    • 2020-07-11
    • 2019-04-30
    相关资源
    最近更新 更多