いくつかのクラスは、例えば
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;
}
を実際のC#に翻訳するために、それは 'ない... while'ループだろう。 –
はい、それは 'do..while'ループになります。しかしラベルと 'goto'sも有効なC#です。私は可能な限りILに似ていると思っています。 – vyrp