在处理程序中取消订阅其他处理程序时是否始终调用所有事件处理程序?

时间:2017-04-02 09:14:26

标签: c# events

我编写了一个测试,显示在调用事件时,即使其中一个处理程序(不是最后一个)取消订阅所有处理程序,也会调用所有处理程序。

测试在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));
        }
    }
}

2 个答案:

答案 0 :(得分:2)

是的,这是预期的行为,因为多播委托是不可变的。

来自MSDN

  

代表是不可改变的;一旦创建,代理的调用列表就不会改变。

因此,任何正在进行的多播委托(事件处理程序)调用都不会受到委托中的订阅和取消订阅的影响(或者通常在调用第一个和最后一个委托之间)。将调用Invoke()时的所有处理程序(禁止处理程序中的任何抛出异常)。 this answer中的更多详细信息。

答案 1 :(得分:1)

  

我的问题是:这种行为是否被预期并记录在案(我还没有发现任何提及),是否可以依靠它?

是的,因为您在取消订阅之前触发了所有3个侦听器

第一行

// Act
sut.TriggerEvent();

是导致它们执行的原因,因此它们将在第二个侦听器取消订阅之前被调用