.net默认事件处理程序

时间:2011-04-25 22:06:25

标签: c# .net design-patterns

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

public class Global
{
    public static event EventHandler<MyEventArgs> Message;
    public static void ShowMessage();
}

现在假设我有一个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
}

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

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

5 个答案:

答案 0 :(得分:3)

  

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

是的,你担心这个是对的。这类事件订阅非常易变,事件源总是超过订阅者。我知道框架中只有一个类就是SystemEvents。问题是每个订户必须非常小心地取消订阅它的生命周期结束或对象永远保持引用。内存泄漏非常难以诊断。

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

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

public interface IGlobalNotification {
    event EventHandler Disposed;
    void OnMessage(MyEventArgs arg);
}

现在您可以使用表单实现界面:

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

    void IGlobalNotification.OnMessage(MyEventArgs arg) {
        // do something
    }
}

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>();
}

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

答案 1 :(得分:2)

我会让派生类覆盖Global_Message。对事件的订阅是通用的,为什么要再次在每个孩子中实现它?它还为您提供了调用base.Global_Message(sender, e)的选项,以防您的子类只想添加一些装饰并使用默认行为。

答案 2 :(得分:0)

我更喜欢你的第二个例子,因为这样,扩展你的基类的类只需要覆盖一个方法,而不必从事件中删除基类添加的处理程序。

答案 3 :(得分:0)

关键是添加虚拟关键字,以便派生类型可以覆盖方法,而是调用它们创建的方法。

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

答案 4 :(得分:0)

现在你已经向两者添加了虚拟,我会选择第一个并覆盖订阅该事件的那个,如果他们不想订阅该事件的话。

虽然还有其他选择,但请将其命名为#3。

protected EventHandler GlobalMessageEvent = new EventHandler<MyEventArgs>(Global_Message);
protected virtual void OnSubscribeToMessageEvent() 
{
    // this could be done in the Form_Load() or constructor instead.
    Global.Message += GlobalMessageEvent;
}

然后潜在的继承类可以在某处:(注意 - =

{
    Global.Message -= GlobalMessageEvent;
}