.net 默认事件处理程序

Tom*_*lny 5 .net c# design-patterns

在我的产品中,我需要处理广泛的事件。为此,我使用了这样的代码:

public class Global
{
    public static event EventHandler<MyEventArgs> Message;
    public static void ShowMessage();
}
Run Code Online (Sandbox Code Playgroud)

现在假设我有一个 WinForms 用户界面。在表单的代码中,我将订阅此事件并以某种默认方式处理它(例如,通过使用 System.Windows.Forms.MessageBox.Show() 方法)。现在的问题是如何允许用户创建派生表单并覆盖我的默认 Message 事件处理程序实现?

仅使用自定义实现第二次订阅事件并不能解决问题(将执行两个事件处理程序并可能显示两个消息框)。我看到的选项是:

//call OnSubscribeToMessageEvent() from either form's constructor or OnLoad event handler
protected virtual void OnSubscribeToMessageEvent()
{
    Global.Message += new EventHandler<MyEventArgs>(Global_Message);
}
private void Global_Message(object sender, MyEventArgs e)
{
    //my default implementation
}
Run Code Online (Sandbox Code Playgroud)

或者

//subscribe in either form's constructor or OnLoad event handler
protected virtual void Global_Message(object sender, MyEventArgs e)
{
    //my default implementation
}
Run Code Online (Sandbox Code Playgroud)

哪个版本更好,为什么?或者也许还有其他选择?

Han*_*ant 3

我仍然有一些疑问,因为我从未在任何 .NET 库中见过这样的设计模式

是的,你担心这个是对的。此类事件订阅非常变化无常,事件源总是比订阅者寿命更长。据我所知,框架中只有一个类可以执行此操作,即 SystemEvents。问题在于,每个订阅者在其生命周期结束时都必须非常小心地取消订阅,否则该对象将永远保持引用状态。很难诊断的内存泄漏。

这里更好的模式是使用接口。让我们声明一个:

public class MyEventArgs { /* etc.. */ }

public interface IGlobalNotification {
    event EventHandler Disposed;
    void OnMessage(MyEventArgs arg);
}
Run Code Online (Sandbox Code Playgroud)

现在您可以让表单实现该接口:

public partial class Form1 : Form, IGlobalNotification {
    public Form1() {
        InitializeComponent();
        GlobalMessages.Register(this);
    }

    void IGlobalNotification.OnMessage(MyEventArgs arg) {
        // do something
    }
}
Run Code Online (Sandbox Code Playgroud)

Register 方法向 GlobalMessages 类注册表单,Dispose 事件确保该类可以检测到表单正在死亡:

public static class GlobalMessages {
    public static void Register(IGlobalNotification listener) {
        listener.Disposed += delegate { listeners.Remove(listener); };
        listeners.Add(listener);
    }
    public static void Notify(MyEventArgs arg) {
        foreach (var listener in listeners) listener.OnMessage(arg);
    }

    private static List<IGlobalNotification> listeners = new List<IGlobalNotification>();
}
Run Code Online (Sandbox Code Playgroud)

调用 GlobalMessages.Notify() 以使 OnMessage() 方法在所有实时表单实例中运行。这种方法的主要优点是客户端程序员永远不会搞砸。