Как цикломатическая сложность может быть равна 27 в методе с 13 подписками на обработчики событий?

У нас есть этот код, вроде:

private void InitializeEvents()
{
    this.Event1 += (s,e) => { };
    this.Event2 += (s,e) => { };
    this.Event3 += (s,e) => { };
    this.Event4 += (s,e) => { };
    this.Event5 += (s,e) => { };
    this.Event6 += (s,e) => { };
    this.Event7 += (s,e) => { };
    this.Event8 += (s,e) => { };
    this.Event9 += (s,e) => { };
    this.Event10 += (s,e) => { };
    this.Event11 += (s,e) => { };
    this.Event12 += (s,e) => { };
    this.Event13 += (s,e) => { };
}

Анализ кода в VS10 Ultimate говорит о «цикломатической сложности 27». Удаление одной из строк делает цикломатическую сложность 25.

Ветвление не происходит, так как же это возможно?


person Johann Gerell    schedule 20.04.2012    source источник


Ответы (3)


Помните, что анализ кода рассматривает IL в вашей сборке, а не исходный код. В IL нет ничего, что изначально поддерживало бы лямбда-выражения, поэтому они являются конструкцией компилятора. Подробнее о выводе можно узнать здесь. Но в основном ваше лямбда-выражение превращается в частный статический класс, который является анонимным делегатом. Однако вместо создания экземпляра анонимного делегата каждый раз, когда на него ссылаются в коде, делегат кэшируется. Таким образом, каждый раз, когда вы назначаете лямбда-выражение, он проверяет, был ли создан экземпляр этого лямбда-делегата, если это так, он использует кэшированный делегат. Это генерирует if/else в IL, увеличивая сложность на 2. Таким образом, в этой функции сложность равна 1 + 2 * (лямбда-выражение) = 1 + 2 * (13) = 27, что является правильным числом.

person user957902    schedule 20.04.2012
comment
+1 за помните, что анализ кода смотрит на IL в вашей сборке, а не на ваш исходный код. В IL нет ничего, что изначально поддерживало бы лямбда-выражения. - person LCJ; 19.11.2012
comment
Лямбды/делегаты КЭШИРУЮТСЯ ТОЛЬКО ЕСЛИ над ними нет замыкания. В противном случае это не так. Это одна из причин (из нескольких) того, что лямбда-выражения дороги. Также задействованы JIT, распределение и GC, но это тема для другого обсуждения. Я исправил больше проблем с производительностью, связанных с лямбда-выражениями с замыканиями в горячих методах, которые я могу сосчитать. - person Dave Black; 26.04.2017

Компилятор C# на самом деле генерирует несколько довольно «интересных» IL для анонимных методов, включая лямбда-выражения. Для каждого из них он создает частное поле, затем, прежде чем присвоить его значение в методе-потребителе, он проверяет, является ли значение нулевым, что добавляет ветвь If к скомпилированному методу. Инструмент метрик кода должен игнорировать это (http://social.msdn.microsoft.com/Forums/eu/vstscode/thread/8c17f569-5ee3-4d26-bf09-4ad4f9289705, https://connect.microsoft.com/VisualStudio/feedback/details/555560/method-using-many-lambda-expressions-causes-high-cyclomatic-complexity), и мы можем надеяться, что в конечном итоге это произойдет. На данный момент вам в значительной степени придется игнорировать проблему, если вы чувствуете, что это ложное срабатывание.

person Nicole Calinoiu    schedule 20.04.2012

Лучше всего предположить, что это, вероятно, связано с тем, что приведенные выше операторы преобразуются в формат средства доступа к событию, а именно

class MyClass
{
  private event EventHandler MyPrivateEvent;

  public event EventHandler MyEvent
  {
    add
    {
      MyPrivateEvent += value;
    }
    remove
    {
      MyPrivateEvent -= value;
    }
  }
}

См. http://msdn.microsoft.com/en-us/magazine/cc163533.aspx и http://www.switchonthecode.com/tutorials/csharp-tutorial-event-accessors для обсуждения формата доступа к событиям.

person Andrew Jones    schedule 20.04.2012