我编写了一个测试,显示在调用事件时,即使其中一个处理程序(不是最后一个)取消订阅所有处理程序,也会调用所有处理程序。
测试在this gist,在下面重复。
我的问题是:这种行为是否被预期并记录在案(我没有发现任何提及),是否可以依靠它?或者也许是因为我编写测试的一些特点?
namespace EventUnsubscriptionTest
{
using System;
using NUnit.Framework;
public class EventSource
{
public event Action Event;
public void TriggerEvent()
{
this.Event?.Invoke();
}
}
public class EventSourceTests
{
[Test]
public void When_Unsubscribing_Should_InvokeAllBeforeUnsubscribing()
{
// Arrange
var invokeCounter = 0;
var listener1InvokedOrder = 0;
var listener2InvokedOrder = 0;
var listener3InvokedOrder = 0;
var sut = new EventSource();
Action listener1 = null;
Action listener2 = null;
Action listener3 = null;
listener1 = () => listener1InvokedOrder = ++invokeCounter;
listener2 = () =>
{
listener2InvokedOrder = ++invokeCounter;
sut.Event -= listener1;
sut.Event -= listener2;
sut.Event -= listener3;
};
listener3 = () => listener3InvokedOrder = ++invokeCounter;
sut.Event += listener1;
sut.Event += listener2;
sut.Event += listener3;
// Act
sut.TriggerEvent();
// Assert
Assert.That(listener1InvokedOrder, Is.EqualTo(1));
Assert.That(listener2InvokedOrder, Is.EqualTo(2));
Assert.That(listener3InvokedOrder, Is.EqualTo(3));
// Ensure that we actually unsubscribed
sut.TriggerEvent();
Assert.That(invokeCounter, Is.EqualTo(3));
}
}
}
答案 0 :(得分:2)
是的,这是预期的行为,因为多播委托是不可变的。
来自MSDN:
代表是不可改变的;一旦创建,代理的调用列表就不会改变。
因此,任何正在进行的多播委托(事件处理程序)调用都不会受到委托中的订阅和取消订阅的影响(或者通常在调用第一个和最后一个委托之间)。将调用Invoke()
时的所有处理程序(禁止处理程序中的任何抛出异常)。 this answer中的更多详细信息。
答案 1 :(得分:1)
我的问题是:这种行为是否被预期并记录在案(我还没有发现任何提及),是否可以依靠它?
是的,因为您在取消订阅之前触发了所有3个侦听器。
第一行
// Act
sut.TriggerEvent();
是导致它们执行的原因,因此它们将在第二个侦听器取消订阅之前被调用