2017-03-26 9 views
3

私は、1より大きい循環的複雑度を持つものをテストする目的で、循環的複雑さを精査しています。なぜイベントの周期的複雑さは2ですか?

私が見ているのは、非常に単純なイベントの加算アクセサーが2の循環的複雑度を持つということです。どうしてこれなの?これは、コールバックメソッドがすでに登録されているかどうかをaddメソッドが最初に確認するためですか?

私は非常に単純な電卓アプリケーションを作成してこの動作を再現しました。 Calculate()メソッドが完了すると発生するCalculateCompleteというイベントがあります。

enter image description here

答えて

1

いくつかのクラスは、例えば

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

ために、いくつかのイベントである場合、イベントのために生成されたILコードは、メソッドを追加している。

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 

注意におけるそのメソッドの終わりにはInterlocked.CompareExchange()コールがあり、その後に "等しくなければブランチ"が続きます。だから、支店があるので、循環的な複雑さは2になります。

なぜそれがそういうのでしょうか?理由は代理人が不変であるためです。デリゲートにメソッドを追加すると、元のデリゲートは変更されませんが、実際には既存のデリゲートと提供されたメソッドのデリゲートが作成され、イベントに再割り当てされます。 Delegate.Combineを参照してください。

さらに、新旧のデリゲート間のスワップはスレッドセーフでなければならないため、Interlocked.CompareExchangeです。スワップに失敗した場合は、もう一度お試しください。支援するため

、私はC#にILを翻訳しました:

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; 
} 
+0

を実際のC#に翻訳するために、それは 'ない... while'ループだろう。 –

+0

はい、それは 'do..while'ループになります。しかしラベルと 'goto'sも有効なC#です。私は可能な限りILに似ていると思っています。 – vyrp

関連する問題