Как передать делегат в качестве аргумента для подписки в качестве обработчика событий?
У меня есть внешнее приложение, которое предоставляет событие StkQuit
.
Я подписываюсь на это событие в статическом классе, который обрабатывает всю связь между моим приложением и [внешним] приложением. Я хотел бы подписаться на событие StkQuit
, используя другой обработчик, который находится на моем классе формы.
Этот обработчик проинформирует пользователя о том, что внешнее приложение закрыто. Я хотел бы иметь общий метод в статическом классе SubscribeToStkQuit
, который принимает делегат в качестве параметра и подписывает этот делегат (ссылающийся на обработчик в моем классе формы) на событие StkQuit
.
Возможно ли это? Является ли это самым элегантным/упрощенным способом достижения такой функциональности?
Мой пример кода:
Класс формы
public delegate void dForceClose();
public void SubscribeToStkQuit(dForceClose forceClose)
{
UtilStk.SubscribeToStkQuit(forceClose = new dForceClose(ForceClose));
}
private void ForceClose()
{
MessageBox.Show("TEST");
}
Статический класс
private static AgUiApplication _stkUiApplication;
public static void SubscribeToStkQuit(Delegate subscribeHandler)
{
_stkUiApplication.OnQuit += subscribeHandler;
}
[Обновление]
В соответствии с комментариями я обновил код следующим образом:
public delegate void dForceClose(object sender, EventArgs e);
public void SubscribeToStkQuit(dForceClose forceClose)
{
UtilStk.SubscribeToStkQuit(forceClose = new dForceClose(ForceClose));
}
private void ForceClose(object sender, Eventargs e)
{
MessageBox.Show("TEST");
}
Я все еще получаю исключение. Любые идеи?
[Обновить # 2]
У меня все еще есть проблемы с этим. В моем статическом классе у меня есть обработчик уже, когда срабатывает OnQuit
:
private static AgUiApplication _stkUiApplication;
public static bool CheckThatStkIsAvailable()
{
object stkApplication = null;
try
{
stkApplication = Marshal.GetActiveObject(_stkProgId);
_stkUiApplication = stkApplication as AgUiApplication;
_stkUiApplication.OnQuit += StkQuit;
return true;
}
catch (Exception)
{
// handle this
}
}
private static void StkQuit()
{
_stkUiApplication.OnQuit -= StkQuit;
Marshal.FinalReleaseComObject(_stkUiApplication);
Marshal.FinalReleaseComObject(_stkRoot);
}
Это прекрасно работает. Поэтому я решил создать публичное свойство для _stkUiApplication и подписаться с классом формы таким же образом:
Статический класс
public static AgUiApplication StkUiApplication
{
get { return _stkUiApplication; }
}
Класс формы
private void SubscribeToStkQuit()
{
UtilStk.StkUiApplication.OnQuit += StkQuit;
}
private void StkQuit()
{
MessageBox("TEST");
}
Ответы
Ответ 1
Если вы подписаны на статическое событие с помощью метода экземпляра, то экземпляр не будет собираться мусором до тех пор, пока не будет установлено статическое событие (если вы не отмените подписку).
Это приведет к утечке памяти.
Кроме того, проблема с вашим кодом заключается в том, что подпись ForceClose()
не соответствует _stkUiApplication.OnQuit
, она должна быть ForceClose(object sender, SomeKindOfEventArgs e)
Это должно быть UtilStk.SubscribeToStkQuit(forceClose => (s, e){ForceClose();});
EDIT:
Ссылка на внешнее приложение статична:
private static AgUiApplication _stkUiApplication;
поэтому он будет работать в течение всего времени вашего приложения. Добавление обработчика событий в
_stkUiApplication.OnQuit
передает ссылку на метод в экземпляре вашего класса формы. Эта ссылка будет рассмотрена на весь срок службы вашего приложения, поэтому сбор мусора невозможен.
Чтобы справиться с этой ситуацией, либо явно де-регистроить (- =) обработчик при размещении объекта прослушивания, либо обрабатывать статические события со статическими обработчиками.
Я ошибочно подумал, что сначала вы описываете веб-формы, что немного меняет вещи (вы можете только создать экземпляр Form
), но вышеприведенное имеет значение независимо. И это хорошая практика.
Чтобы решить вашу текущую проблему:
Вам нужно ввести параметр, переданный в:
public static void SubscribeToStkQuit(Delegate subscribeHandler)
{
_stkUiApplication.OnQuit += subscribeHandler;
}
будет типом _stkUiApplication.OnQuit
Что-то вроде
public static void SubscribeToStkQuit(EventHandler<EventArgs> subscribeHandler)
{
_stkUiApplication.OnQuit += subscribeHandler;
}
Затем вы можете сделать это:
public void SubscribeToStkQuit()
{
UtilStk.SubscribeToStkQuit((sender, args) => ForceClose(sender, args));
}
Ответ 2
Да, вы можете!
Но лучше использовать действия и функции, которые являются гибкими с точки зрения параметризации (все они находятся в пространстве имен System).
Во всяком случае, у меня есть другое предложение: почему бы вам не использовать средства доступа к событиям? Вы можете создать его в своем статическом классе:
public event EventHandler Quit { add { _stkUiApplication.OnQuit += value; } remove { _stkUiApplication.OnQuit -= value; } }
О действиях и функциях, это тоже делегаты, поэтому вы можете использовать их везде как входные параметры для любой делегирования.
Возможно, вы можете использовать средство доступа к событиям и сделать это с помощью .NET-способа, не так ли?