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

У меня есть внешнее приложение, которое предоставляет событие 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-способа, не так ли?