Если есть какой-то класс с каким-то событием, например
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. Если замена не удалась, попробуйте еще раз.
Чтобы помочь, я перевел IL на 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;
}
person
vyrp
schedule
26.03.2017