Почему цикломатическая сложность кода с `switch` выше, чем с 'if-else'

У меня есть два примера функций TestComplexityIf и TestComplexitySwitch. Инструмент VisualStudio-2017 «Вычислить метрики кода» сообщает о цикломатической сложности 10 для функции с оператором switch и 7 для функции с if-else. Интересно, как рассчитывается сложность TestComplexitySwitch().

private static void TestComplexityIf(String arg)
{
    if (arg == "a")
    { }
    else if (arg == "b")
    { }
    else if (arg == "c")
    { }
    else if (arg == "d")
    { }
    else if (arg == "d")
    { }
    else if (arg == "f")
    { }
    else if (arg == "g")
    { }
}

private static void TestComplexitySwitch(String arg)
{
    switch (arg)
    {
        case "a":
            break;
        case "b":
            break;
        case "c":
            break;
        case "d":
            break;
        case "e":
            break;
        case "f":
            break;
        case "g":
            break;
    }
}

Кроме того, если я прокомментирую последний случай, сложность внезапно изменится на 6.

private static void TestComplexitySwitch(String arg)
{
    switch (arg)
    {
        case "a":
            break;
        case "b":
            break;
        case "c":
            break;
        case "d":
            break;
        case "e":
            break;
        case "f":
            break;
        //case "g":
            //break;
    }
}

person FaisalM    schedule 18.03.2020    source источник
comment
Пробовали ли вы добавить случай по умолчанию? Меня интересует, что это должно делать со сложностью.   -  person Fabian Claasen    schedule 18.03.2020
comment
Глубина отступа играет роль в этом значении, которое выше для switch. Может быть так просто.   -  person Markus Deibel    schedule 18.03.2020
comment
острый И.О. тест   -  person TheGeneral    schedule 18.03.2020
comment
Возможно, это больше связано с тем, как он компилируется, считает каждую ветвь, он ближе к значениям, которые вы описываете. Хотя в последнем примере я насчитал 7, а не 6... в любом случае, это было весело, пока это длилось, кто знает... я думаю, у вас должны быть более важные вещи, о которых нужно беспокоиться.   -  person TheGeneral    schedule 18.03.2020
comment
@Fabian со значением регистра по умолчанию (и всех случаев от a до g) равно 10.   -  person FaisalM    schedule 18.03.2020
comment
Вы пробовали менее вырожденную версию кода, например. вернуть другое значение из каждой ветки? Текущая реализация в основном ничего не делает и может быть оптимизирована, как показывает ссылка Майкла Рэндалла.   -  person Jonas Høgh    schedule 18.03.2020
comment
@Майкл. Похоже, что когда количество операторов case > 6, .NET использует хэш для определения правильного case. Это добавляет дополнительные «3» дополнительных пути. Когда последний case закомментирован, сгенерированный код использует if со сравнением строк вместо сравнения хэшей.   -  person FaisalM    schedule 18.03.2020


Ответы (1)


Инструмент Visual Studio Cyclomatic Complexity (CC) вычисляет значения из кода IL и, таким образом, зависит от сведений о компиляторе.

Здесь вы натыкаетесь на деталь производительности компилятора: когда переключатель над строкой имеет строго более 6 случаев, компилятор создает хеш-таблицу для быстрого поиска строки. Эта хеш-таблица не появляется в коде C#, а только в коде IL. Код IL становится более сложным для обработки этой хеш-таблицы, и вы получаете неожиданное значение CC. Эта ситуация хорошо иллюстрируется эту ссылку от Michael Рэндалл.

В качестве альтернативы вы можете использовать инструмент NDepend для вычисления CC из исходного кода C#, визуализировать значения CC в цветной древовидной карте и запустить некоторые правила запрещают слишком сложные методы.

Цветная древовидная карта цикломатической сложности

(Отказ от ответственности, я работаю в NDepend)

person Patrick from NDepend team    schedule 19.03.2020