如何将委托作为参数传递给订阅作为事件处理程序?

时间:2011-01-27 13:26:50

标签: c# events delegates

我有一个提供事件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");
}

2 个答案:

答案 0 :(得分:3)

如果使用实例方法订阅静态事件,则在处理静态事件之前不会对实例进行垃圾回收(除非您取消订阅)。

这会导致内存泄漏。

除此之外,代码的问题在于ForceClose()的签名与_stkUiApplication.OnQuit的签名不一致ForceClose(object sender, SomeKindOfEventArgs e)

应为UtilStk.SubscribeToStkQuit(forceClose => (s, e){ForceClose();});

编辑:

对外部应用的引用是静态的:

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

答案 1 :(得分:3)

是的,你可以!

但最好使用在参数化方面灵活的动作和函数(所有这些都在System命名空间中)。

无论如何,我还有另一个建议:为什么不使用事件访问器?您可以在静态类中创建一个:

public event EventHandler Quit { add { _stkUiApplication.OnQuit += value; } remove { _stkUiApplication.OnQuit -= value; } }

关于操作和功能,这些也是委托,因此您可以将它们作为任何委托的输入参数随处使用。

也许您可以使用事件访问器并以.NET方式执行,不是吗?