Полные списки выражений для кода C#, которые +1 за цикломатическую сложность

Мне нужно построить блок-схему управления (простой блок-схему с узлами и ребрами) для каждого метода в моем проекте 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, я не понимаю, какие другие выражения способствуют сложности.

Я провел поиск по многим источникам, но не смог получить полный список всех выражений, которые будут учитываться.

Кто-нибудь может помочь в этом? Или есть какой-либо инструмент, который я могу создать блок-схему управления из кода С#?

Заранее спасибо...


person shennyL    schedule 05.12.2011    source источник
comment
У меня нет ответа на этот вопрос, но я предполагаю, что в дополнение к веткам переключателя VS подсчитывает случаи, когда может быть выдано исключение. Например, если elementId == null, метод выдаст исключение NullReferenceException при попытке выполнить вторую строку. Это можно рассматривать как отдельный путь выполнения.   -  person Gebb    schedule 05.12.2011


Ответы (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 истинно, сообщение будет отображаться (первый возможный путь), а если someCondition ложно, сообщение не будет отображаться (второй возможный путь).

Если вы посмотрите на реализацию Dispose в Windows Forms, она будет выглядеть так:

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

Здесь цикломатическая сложность равна 3. В случае && оба значения должны быть истинными, чтобы вычислить выражение внутри. Это означает, что если оба disposing истинны, и (components != null) верны, у вас есть первый путь. Если disposing ложно, у вас есть второй путь. Третий путь исходит из того факта, что components может быть нулевым, и поэтому он будет оцениваться как ложный. Следовательно, у вас есть цикломатическая сложность, равная трем.

В случае switch вы получаете +1, а за каждое casedefault), которое появляется внутри, вы получаете +1. В случае вашего метода у вас есть шесть операторов case и один оператор 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);                   
}        

Я могу ошибаться в нескольких моментах, но в основном это идея расчета цикломатической сложности. Вы также должны понимать, что есть случаи, когда вы не можете уменьшить это (если вам нужно использовать переключатель / регистр, CC увеличивается). Кроме того, если ваши переменные имеют ужасное имя (как если бы вы пытались запутать свой код), цикломатическая сложность может вернуть более низкое значение, потому что она не может понять, что ваше имя ужасно. Именование может увеличить сложность для вас, и если вы не используете комментарии в своем коде, через 6 месяцев вам будет трудно понять, почему цикломатическая сложность равна 3, но вы просто не можете понять то, что пишется.

person Husein Roncevic    schedule 18.01.2012