Почему происходит изменение цикломатической сложности при перестановке операторов switch с другими зависимыми факторами?

CC для приведенного ниже метода будет сгенерирован как 9

    Public Enum Fruits
        Apple
        Pineapple
        Banana
        PassionFruit
        Orange
        Melon
        Grape
        Mango
    End Enum


  Private Function Fruity(ByVal fruitType As Fruits)

        Select Case fruitType
            Case Fruits.Orange, Fruits.Banana
            Case Fruits.Melon
            Case Fruits.Mango
            Case Fruits.Pineapple
            Case Fruits.Passionfruit
            Case Fruits.Melon, Fruits.Apple
        End Select
    End Function

но когда значения членов перечисления определяются пользователем, CC метода Fruity увеличивается до 14.

 Public Enum Fruits
        Apple = 1
        Pineapple = 3
        Banana = 4
        Passionfruit = 5
        Orange = 7
        Melon = 9
        Grape = 11
        Mango = 13
    End Enum

Теперь предположим, что я изменил оператор select-case, как показано ниже, тогда CC метода Fruity изменится на 7, но есть небольшое снижение индекса ремонтопригодности по сравнению с указанным выше select-case.

 Select Case true
       Case fruitType= Fruits.Orange or fruitType= Fruits.Banana
       Case fruitType=Fruits.Melon
       Case fruitType=Fruits.Mango
       Case fruitType=Fruits.Pineapple
       Case fruitType=Fruits.Passionfruit
       Case fruitType=Fruits.Melon, Fruits.Apple
  End Select

В последнем сценарии я меняю тип данных параметра на целое число и получаю доступ к другому Enum, называемому овощами, который имеет значение 50. Теперь CC метода Fruity взлетает до 52. Но если я изменю значение Potato на 0 или 1 тогда CC остается на 15.

  Public Enum Vegetables
   Potato = 50
  End Enum

  Private Function Fruity(ByVal fruitType As Integer)

        Select Case fruitType
            Case Fruits.Orange, Fruits.Banana
            Case Fruits.Melon
            Case Fruits.Mango
            Case Vegetables.Potato
            Case Fruits.Passionfruit
            Case Fruits.Melon, Fruits.Apple
        End Select
    End Function

Все они были протестированы в версии системы VS 2008 Team с 3.5SP1. Я хотел бы знать, почему в этих 4 сценариях есть колебания в CC.

Это как-то связано со 2-м пунктом, приведенным ниже? Потому что, если я уберу случаи провала в 4-м сценарии, CC упадет до 9. Так это ошибка в VS 2008?

Специальное примечание по Visual Studio 2010

При расчете метрик кода с помощью Visual Studio 2010 могут быть различия, которые не применимы к Visual Studio 2008. Электронная документация (http://msdn.microsoft.com/en-us/library/ee703787.aspx) приводит следующие причины:

  1. Функция содержит один или несколько блоков catch. В предыдущих версиях Visual Studio блоки catch не учитывались при расчете. В Visual Studio 2010 сложность каждого блока catch добавляется к сложности функции.

  2. Функция содержит оператор switch (Select Case в VB). Различия в компиляторе между Visual Studio 2010 и более ранними версиями могут привести к созданию другого кода MSIL для некоторых операторов switch, содержащих неверные варианты.


person kurozakura    schedule 19.10.2013    source источник


Ответы (1)


Подсчет цикломатической сложности — это довольно простая функция, она просто подсчитывает количество ветвей, которые появляются внутри IL, сгенерированного из вашего кода. Каждая ветвь увеличивает счет на 1.

Если вы хотите просмотреть этот IL, запустите утилиту ildasm.exe из командной строки Visual Studio. Когда вы сравните IL для этих разных сниппетов, сразу станет понятно, почему количество сложностей меняется таким образом.

  • Ваша первая версия создает Opcodes.Switch с одной записью для каждого перечисления. Каждая запись в таблице переключателей соответствует оператору Case и увеличивает сложность на 1.
  • Ваша следующая версия увеличит размер таблицы переключения. Он добавляет записи для отсутствующих значений перечисления. Это неуместно увеличивает количество сложностей, инструмент просто недостаточно умен, чтобы распознавать фиктивные записи, и не может легко это сделать.
  • В вашей последней версии слишком много пробелов в значениях перечисления. Компилятор распознает, что Opcodes.Switch больше не действует, и генерирует совершенно другой код, теперь используя набор тестов If/ElseIf. Снижение количества сложностей.

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

person Hans Passant    schedule 19.10.2013