C# 代码表达式的完整列表,其中 +1 表示循环复杂度

发布于 2024-12-19 15:04:37 字数 1735 浏览 3 评论 0原文

我需要为 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# 代码生成控制流程图?

先感谢您...

I need to construct a Control Flow Diagram (simple flow graph with nodes and edges) for each method in my C# project in order to demonstrate the graph-way to calculate cyclomatic complexity.

I first counted the cyclomatic complexity using VS 2010, then I construct the graph to make sure the result value is same as the one counted from VS. However, I met some problem here because I not sure which expression is actually consider a +1 for cyclomatic complexity.

Let's look at one example here:

 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);
    }

For this method, VS calculated the cyclomatic complexity as 10. However, there are only 7 case statement, I don't understand what other expressions contribute to the complexity.

I had search through many sources, but could not get a complete lists of all expressions which will be counted.

Can anyone help on this? Or there is any tool which i can generate Control Flow Diagram from C# code?

Thank you in advance...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

-柠檬树下少年和吉他 2024-12-26 15:04:37

您首先应该做的是尝试使用图表来可视化圈复杂度。在查看您的代码时,我设法计算出 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。两个值都必须为 true 才能计算内部表达式。这意味着如果 both dispose 为 true (components != null) 为 true,则您拥有第一条路径。如果 dispose 为 false,则您有第二条路径。第三条路径来自以下事实:components 可以为 null,因此其计算结果为 false。因此,你的圈复杂度为三。

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

正如我在一开始所说的,如果您尝试用图表来可视化您的代码,它可以像这样分解(我将我的注释添加到您的代码中并删除您的注释):

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 会增加)。此外,如果你的变量有糟糕的命名(就像你试图混淆你的代码一样),圈复杂度可能会返回较低的值,因为它无法理解你的命名是糟糕的。命名会增加你的复杂性,如果你不在代码中使用注释,6 个月后你将很难弄清楚为什么圈复杂度是 3,但你就是无法理解正在编写的东西。

What you should do first is try and visualize cyclomatic complexity using graphs. While going through you code I managed to calculate 10. In order to better understand this look at the following:

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

This has cyclomatic complexity of 1, because there is only one possible path here, and that is to display the message.

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

This time we have cyclomatic complexity of 2. +1 is added to if, while, for, foreach. In this case there are two paths. If someCondition is true the message will be displayed (first possible path), and if someCondition is false the message will not be displayed (second possible path).

If you have a look at the implementation of Dispose in Windows Forms it looks like this:

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

Here you have cyclomatic complexity of 3. In case of && both values must be true in order to evaluate the expression inside. That means that if both disposing is true and (components != null) is true you have the first path. If disposing is false you have second path. The third path comes from the fact that components can be null and as such it would evaluate to false. Therefore you have cyclomatic complexity of three.

In case of switch you get +1, and for each case (and default) that appears inside you get +1. In case of your method, you have six case statements and one default plus switch, making this 8 in total.

As I said in the beginning, if you try and visualize you code in terms of graph it can be broken down like this (I am adding my comments to your code and removing your comments):

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);                   
}        

I might be wrong in a few points, but basically this is the idea behind the calculation of cyclomatic complexity. You should also understand that there are cases when you cannot decrease this (if you need to use switch/case, the CC increases). Additionally, if your variables have awful naming (as if you tried to obfuscate your code) cyclomatic complexity might return lower value because it cannot understand that you naming is awful. Naming can increase a complexity for you and if you don't use comments in your code, after 6 months you will have hard time figuring why cyclomatic complexity is 3 but you just cannot understand a things that is being written.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文