.net обработчик событий по умолчанию - PullRequest
4 голосов
/ 26 апреля 2011

В моем продукте мне нужны широкие события процесса. Для этого я использовал такой код:

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
}

Какая версия лучше и почему? Или, может быть, есть другие варианты?

Ответы [ 5 ]

3 голосов
/ 26 апреля 2011

У меня все еще есть некоторые сомнения, поскольку я никогда не видел такого шаблона проектирования ни в одной библиотеке .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 () выполнялся во всех экземплярах живых форм. Основным преимуществом этого подхода является то, что клиентский программист никогда не сможет испортить.

2 голосов
/ 26 апреля 2011

Я бы позволил производному классу переопределить Global_Message.Подписка на мероприятие носит общий характер, и почему вы хотели бы внедрить ее снова в каждом ребенке?Это также дает вам возможность вызвать base.Global_Message(sender, e) в случае, если ваш дочерний класс просто хочет добавить к нему какое-то украшение и в противном случае использовать поведение по умолчанию.

0 голосов
/ 26 апреля 2011

Теперь, когда вы добавили виртуальный к обоим, я бы выбрал первый и переопределил тот, который подписывается на событие, если они не хотели, чтобы событие подписывалось.

Хотя есть и другой вариант, назовите его # 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;
}
0 голосов
/ 26 апреля 2011

Ключ добавляет ключевое слово virtual , чтобы производный тип мог переопределить метод, и вместо этого будет вызван созданный ими метод.

//subscribe in either form's constructor or OnLoad event handler
protected virtual void Global_Message(object sender, MyEventArgs e)
{
    //my default implementation
}
0 голосов
/ 26 апреля 2011

Я бы предпочел ваш второй пример, так как классы, расширяющие ваш базовый класс, должны переопределять только один метод и не должны удалять обработчик, добавленный базовым классом, из события.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...