How can the cyclomatic complexity be 27 in a method with 13 event handler subscriptions?
Asked Answered
T

3

8

We have this code, sortof:

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

Code analysis in VS10 Ultimate says "cyclomatic complexity of 27". Removing one of the lines makes the cyclomatic complexity 25.

There's no branching going on, so how is this possible?

Tenorite answered 20/4, 2012 at 10:5 Comment(0)
C
17

Remember that the Code Analysis is looking at the IL in your assembly, not your source code. There is nothing in the IL that natively supports lambda expressions, so they are a construct of the compiler. You can find the specifics of what is output here. But basically your lambda expression is turned into a private static class that is an anonymous delegate. However, rather that create an instance of the anonymous delegate every time it is referenced in code, the delegate is cached. So each time you assign a lambda expression, it does a check to see an instance of that lambda delegate has been created, if so it uses the cached delegate. That generates an if/else in the IL increasing the complexity by 2. So in this functions complexity is 1 + 2*(lambda express) = 1 + 2 *(13) = 27 which is the correct number.

Conformance answered 20/4, 2012 at 11:59 Comment(2)
+1 for "Remeber that the Code Analysis is looking at the IL in your assembly, not your source code. There is nothing in the IL that natively supports lambda expressions"Sterne
Lambdas/delegates are ONLY CACHED IF there is no closure over them. Otherwise, they are not. This is one reason (of several) lambdas are expensive. There is also JIT, allocation and GC involved - but that is for another discussion. I've fixed more performance problems related to lambdas with closures in "hot" methods that I can count.Rothstein
E
3

The C# compiler actually generates some fairly "interesting" IL for anonymous methods, including lambdas. For each one, it creates a private field then, before assigning its value in the consuming method, it checks if the value is null, which adds an If branch to the compiled method. The code metrics tool ought to ignore this (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), and we can hope that it eventually will. For now, you pretty much have to ignore the problem if you feel that it is a false positive.

Equestrian answered 20/4, 2012 at 12:5 Comment(0)
K
1

Best guess is that this is probably due to the statements above being converted to the event accessor format, viz

class MyClass
{
  private event EventHandler MyPrivateEvent;

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

See http://msdn.microsoft.com/en-us/magazine/cc163533.aspx and http://www.switchonthecode.com/tutorials/csharp-tutorial-event-accessors for discussions on the event accessor format.

Kepner answered 20/4, 2012 at 10:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.