9

有什么聪明的方法可以避免在以通用方式调用事件之前测试事件的无效性的冗长吗?很明显,如果我打电话给一个代表,我希望它被分配。
(如果我真的想/需要测试它的无效性,我最终可以明确地做,但是系统地进行这个测试有点乏味和冗长。)

public delegate void ResetTradesDelegate();
public ResetTradesDelegate ResetTradesEvents;

public void OnSessionRxAdmMessage(IVfxFixAppSession session, FixMessage msg)
{    
    if (ResetTradesEvent != null)  //<-- Is there "any" a way not to write this test explicitly for each delegate ?
       ResetTradesEvent();
}
4

5 回答 5

15
public event EventHandler NoDataEventHandler = delegate{};

以这种方式声明事件意味着它永远不会为空。至少,它将始终连接一个无操作事件处理程序。

在你的情况下,可能

public event ResetTradesDelegate ResetTradesEvents = delegate{};

触发一个事件总是会有一个与之相关的竞争条件。您要么冒险尝试在其为空时调用委托,要么在事件被取消挂钩后调用委托。Eric Lippert 在这里写了一篇关于这个主题的非常全面的文章。上面的技术仍然受到第二种竞争条件的影响,因此事件处理程序需要在事件被取消挂钩后被调用。

于 2012-07-12T20:30:11.823 回答
4
static void CallIfNotNull(this Action action)
{
 if (action != null) action();
}

作为一种扩展方法,使用起来相当方便。

于 2012-07-12T20:39:32.117 回答
2

您可以使用始终订阅的无操作事件创建事件处理程序:

public class MyClass
{
    public MyClass()
    {
        this.Event += (sender, e) => ();
    }

    public event EventHandler Event;

    protected virtual void OnEvent()
    {
        this.Event(this, EventArgs.Empty);
    }
}

但是,这需要订阅您的事件并且会导致性能损失,因为 no-op 委托仍将存在于订阅的事件处理程序列表中。


我的偏好是创建一对扩展方法来调用任何事件处理程序,同时执行空安全检查:

public static void Raise(this EventHandler @event, object sender)
{
    if(@event != null)
    {
        @event.Invoke(sender, EventArgs.Empty);
    }
}

public static void Raise<TEventArgs>(
    this EventHandler<TEventArgs> @event,
    object sender,
    TEventArgs args)
    where TEventArgs : EventArgs
{
    if(@event != null)
    {
        @event.Invoke(sender, args);
    }
}

然后可以在库中的任何位置轻松调用它以安全地引发事件:

this.ResetTradesEvent.Raise(this);

它纯粹是语法糖;您仍在对委托进行检查。然而,它是一种很好的可重用方式来包装 C# 语言的这个臭的部分。

于 2012-07-12T21:03:12.423 回答
0
public static void Call(this Action action)
{
    var safeAction = Interlocked.CompareExchange(ref action, null, null);
    if (safeAction != null)
        safeAction();
}
于 2019-06-07T09:53:00.313 回答
0

使用 null 条件运算符可以保持测试但不那么冗长,所以对我有用。我不认为它解决了其他人提到的竞争条件。

ResetTradesEvent?.Invoke();

这是在 Visual Studio 2017 中自动建议的。

于 2019-11-27T17:39:17.323 回答