如何在C#中正确取消订阅事件?

4
我有一个模型类,其中包含一个事件,我从其他类中订阅该事件。我想在每个类中正确地订阅和取消订阅。
  • First I want to guarantee that in MyClass I unsibscribe only once even that code is in few methods.
  • Second there are other classes except MyClass witch use OnMyEvent so I don't want to unintentionally unsibscribe from the event in the class.

     MyClass(IModel model)
    {
      _model = model;
      _model.OnMyEvent +=EventHandle;
    }
    Close()
    {
     _model.OnMyEvent -=EventHandle;
    } 
    Disconnect()
    {
     //I want to check if OnMyEvent has already unsibscribed
     //Moreover OnMyEvent is used in other classes and
     //I don't want to mess up with it here 
     _model.OnMyEvent -=EventHandle;
    }
    
4个回答

11
如果你只订阅一次,无论你退订多少次都没有关系——当你没有订阅时,取消订阅是一个无操作。同样,事件 API 的整个重点在于您不能意外地取消其他订阅(无论是其他类型还是相同类型的其他实例)。
因此,如所示的代码应该是可以的,尽管将这两个调用移动到处理此操作的单个方法中可能值得考虑。不过这可能有点过度设计。
另外,如果您的类型是 IDisposable,请确保在该代码路径中也调用它(通过调用 Close() 方法)。

2
您可以安全地从事件中多次取消订阅相同的处理程序。不需要进行额外的检查,这样反而会适得其反。

1

如果您想确保只取消订阅一次,可以使用GetInvocationList方法:

if (_model.OnMyEvent != null && _model.GetInvocationList().Contains(EventHandle))
{
    _model.OnMyEvent -= EventHandle
}

但是正如其他人提到的那样,您可以多次取消订阅。如果这真的不是问题,请保持原样。我提出的解决方案只是代码噪音。仅在一行中取消订阅更加整洁,当您的类开始增长时,更易于阅读。


通过标准的“事件”API,您将无法访问调用列表。 - Marc Gravell

1

你也可以通过这个声明来控制订阅和取消订阅。但是你还需要遍历字典并手动调用已订阅的委托。

    private Dictionary<string, EventHandler> TestEvents { get; }

    public event EventHandler TestEvent
    {
        add
        {
            string name = value.GetType().FullName;
            if (!TestEvents.ContainsKey(name))
            {
                TestEvents.Add(name, value);
            }
        }
        remove
        {
            string name = value.GetType().FullName;
            if (TestEvents.ContainsKey(name))
            {
                TestEvents.Remove(name);
            }
        }
    }

你可能也需要对 value 进行空值检查。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接