3

循環的複雑度をグラフで計算する方法を示すために、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 と計算しました。ただし、case ステートメントは 7 つしかないため、他のどの式が複雑さに寄与しているのかわかりません。

多くのソースを検索しましたが、カウントされるすべての式の完全なリストを取得できませんでした。

誰でもこれについて助けることができますか?または、C# コードから制御フロー図を生成できるツールはありますか?

前もって感謝します...

4

1 に答える 1

3

最初にすべきことは、グラフを使用して循環的複雑度を視覚化することです。あなたのコードを見ていくうちに、私はなんとか10を計算することができました.これをよりよく理解するために、以下を見てください:

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

ここには可能なパスが 1 つしかなく、メッセージを表示するため、循環的複雑度は 1 です。

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

今回は、循環的複雑度は 2 です。if、while、for、foreach に +1 が加算されます。この場合、2 つのパスがあります。someCondition が true の場合、メッセージは表示され (最初の可能なパス)、someCondition が false の場合、メッセージは表示されません (2 番目の可能なパス)。

Windows フォームでの Dispose の実装を見ると、次のようになります。

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

ここでは、循環的複雑度は 3 です。 && の場合、内部の式を評価するには、両方の値が true でなければなりません。つまり、両方 disposingが trueであり、かつ (components != null)true の場合、最初のパスがあることを意味します。false の場合disposingは、2 番目のパスがあります。components3 番目のパスは、 null になる可能性があるため、false と評価されるという事実に由来します。したがって、循環的複雑度は 3 です。

の場合は+1 を取得し、内部に表示される(および)switchごとに +1を取得します。メソッドの場合、6 つのステートメントと 1 つのplusがあり、合計で 8 つになります。casedefaultcasedefaultswitch

冒頭で述べたように、グラフの観点からコードを視覚化しようとすると、次のように分類できます (コードにコメントを追加し、コメントを削除しています)。

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

いくつかの点で間違っているかもしれませんが、基本的にはこれが循環的複雑度の計算の背後にある考え方です。また、これを減らすことができない場合があることも理解する必要があります (スイッチ/ケースを使用する必要がある場合は、CC が増加します)。さらに、変数の命名がひどい場合 (コードを難読化しようとした場合など)、循環的複雑度は、命名がひどいことを理解できないため、より低い値を返す可能性があります。ネーミングは複雑さを増す可能性があり、コードでコメントを使用しない場合、6 か月後に循環的複雑度が 3 である理由を理解するのに苦労しますが、書かれていることを理解することはできません。

于 2012-01-18T16:32:07.577 に答える