.net 默认事件处理程序

发布于 2024-11-03 21:02:27 字数 981 浏览 1 评论 0原文

在我的产品中,我需要处理范围内的事件。为此,我使用了如下代码:

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

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

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

//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
}

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

In my product I need process wide events. For that I used code like this:

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

Now let's say I have a WinForms user interface. In form's code I will subscribe to this event and handle it in some default way (eg. by using System.Windows.Forms.MessageBox.Show() method). Now the question is how do I allow user to create derived form and override my default Message event handler implementation?

Just subscribing to the event for the second time with custom implementation doesn't solve the problem (both event handlers would be executed and potentially two message boxes shown). The options I see are either:

//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
}

or

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

Which version is better and why? Or maybe there are any other options?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

思念绕指尖 2024-11-10 21:02:27

我仍然有一些疑问,因为我从未在任何 .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 () 方法在所有实时表单实例中运行。这种方法的主要优点是客户端程序员永远不会搞砸。

I still have some doubts as I have never seen such a design pattern in any .NET library

Yes, you're right to worry about this. These kind of event subscriptions are very fickle, the event source always outlives the subscriber. There's only one class in the framework I know that does this, SystemEvents. The problem is that every subscriber has to very carefully unsubscribe itself when its lifetime ends or the object will stay referenced forever. A memory leak that's very hard to diagnose.

A better pattern here is to use an interface. Let's declare one:

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

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

Now you can have a form implement the interface:

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

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

The Register method registers the form with the GlobalMessages class, the Dispose event ensures that the class can detect that the form is dying:

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

Call GlobalMessages.Notify() to get the OnMessage() method to run in all live form instances. The major advantage of this approach is that a client programmer can never screw up.

煞人兵器 2024-11-10 21:02:27

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

I would let the derived class override the Global_Message. The subscription to the event is generic and why would you want to implement it in every child again? It also gives you the option to call base.Global_Message(sender, e) in case your child class just wants to add some decoration to it and use the default behaviour otherwise.

悲歌长辞 2024-11-10 21:02:27

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

I would prefer your second example, as that way, classes that extend your base class only have to override one method and do not have to remove the handler added by the base class from the event.

柠檬心 2024-11-10 21:02:27

关键是添加virtual关键字,以便派生类型可以覆盖该方法,并且将调用它们创建的方法。

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

The key is adding the virtual keyword, so that a derived type can overide the method and the method they created will be called instead.

//subscribe in either form's constructor or OnLoad event handler
protected virtual void Global_Message(object sender, MyEventArgs e)
{
    //my default implementation
}
月光色 2024-11-10 21:02:27

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

虽然还有另一种选择,但称之为#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;
}

Now that you've added virtual to both, I'd go with the first and override the one that subscribes to the event, if they didn't want the event subscribed to.

Though there is another option, call it #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;
}

Then potentially an inherited class could do somewhere: (note the -=)

{
    Global.Message -= GlobalMessageEvent;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文