Why is the cyclomatic complexity of event 2?

I am looking at cyclomatic complexity in an application for the purpose of testing something that has a cyclomatic complexity greater than 1.

What I see is that a very simple access adding element has a cyclical complexity of 2. Why is that? Is it because the add method first checks if the callback method is registered?

I created a very simple calculator app to replicate this behavior. I have a CalculateComplete event that fires when the Calculate () method is complete.

enter image description here

+3


source to share


1 answer


If there is some class with some event like

class SomeClass
{
    public event Action<int> SomeEvent;
}

      

Then the IL code generated for the event add method:

SomeClass.add_SomeEvent:
IL_0000:  ldarg.0     
IL_0001:  ldfld       UserQuery+SomeClass.SomeEvent
IL_0006:  stloc.0     
IL_0007:  ldloc.0     
IL_0008:  stloc.1     
IL_0009:  ldloc.1     
IL_000A:  ldarg.1     
IL_000B:  call        System.Delegate.Combine
IL_0010:  castclass   System.Action<System.Int32>
IL_0015:  stloc.2     
IL_0016:  ldarg.0     
IL_0017:  ldflda      UserQuery+SomeClass.SomeEvent
IL_001C:  ldloc.2     
IL_001D:  ldloc.1     
IL_001E:  call        System.Threading.Interlocked.CompareExchange<Action`1>
IL_0023:  stloc.0     
IL_0024:  ldloc.0     
IL_0025:  ldloc.1     
IL_0026:  bne.un.s    IL_0007
IL_0028:  ret

      

Note that there is a call at the end of the method Interlocked.CompareExchange()

, followed by "branch if not equal". So yes, there is a branch, so the cyclical complexity is 2.



Why is this so, you may ask? The reason is that delegates are immutable. When you add a method to a delegate, you are not modifying the original delegate, but you are actually creating a merged delegate from the existing and provided method and reassigning it to the event. See Delegate.Combine .

Also, the exchange between the new and old delegates must be thread safe, so Interlocked.CompareExchange . If the change is unsuccessful, try again.

To help, I translated IL to C #:

public void add_SomeEvent(Action<int> arg1)
{
    var local0 = this.SomeEvent;
IL_0007:
    var local1 = local0;
    var local2 = (Action<int>)Delegate.Combine(local1, arg1);
    local0 = Interlocked.CompareExchange(ref this.SomeEvent, local2, local1)
    if (local0 != local1) goto IL_0007;
}

      

+1


source







All Articles