如何处理丢失的调用方法

时间:2012-02-03 00:24:07

标签: c# asynchronous

public void RefreshData()
{
    // this is called on UI thread

    List<ds> dataSource;
    GetDsDelegate caller = GetDs;
    caller.BeginInvoke(out dataSource, RefreshCallback, null);
}

private void RefreshCallback(IAsyncResult ar)
{
    // this is called on worker thread

    try
    {
        var result = (AsyncResult)ar;
        var caller = (GetDsDelegate)result.AsyncDelegate;

        List<ds> dataSource;
        var success = caller.EndInvoke(out dataSource, ar);

        if (success)
        {
            BeginInvoke(new Action<List<ds>>(SetGridDataSource), dataSource);
        }
    }
    catch
    {
        // NOTE: It's possible for this form to close after RefreshData is called
        // but before GetDs returns thus the SetGridDataSource method no longer exists.
        // Not catching this error causes the entire application to terminate.
    }

private void SetGridDataSource(List<ds> dataSource)
{
    // this is called on UI thread
    dataGrid.DataSource = dataSource;
}

RefreshData,RefreshCallback和SetGridDataSource都是Windows窗体类的方法。调用RefreshData使用GetDsDelegate委托调用外部方法GetDs。当GetDs完成时,它调用RefreshCallback(现在在一个单独的线程上)。最后,调用SetGridDataSource来完成更新。

除非GetDs延迟且表单关闭,否则所有这些工作正常。然后当GetDs完成并调用RefreshCallback时,SetGridDataSource不再存在。

除了显示的try / catch块之外,还有更好的方法来处理这个条件吗?我宁愿防止错误而不是忽略它。有没有更好的模式可供使用?

修改

当我看到错误时,很明显将if (success)更改为if (success && IsHandleCreated)以防止它,但看起来我似乎做错了,或者至少很尴尬。我也可以用Invoke替换第二个BeginInvoke,因此不需要EndInvoke。我喜欢将逻辑移离表单的想法,但我不知道结果会如何改变。我认为BackgroundWorker也会遇到同样的问题;作为回调不再可访问。我想结果可能会引发一个事件,但这看起来有点抽象。你能详细说明一下还是提供一个例子。

1 个答案:

答案 0 :(得分:0)

该方法确实存在。表单关闭的事实不会改变类或其方法。如果您发布了您所获得的确切异常,它可以帮助某人提供更准确的帮助。

我猜你在表单或其控件关闭后尝试对表单执行某些操作时会得到ObjectDisposedException

如果是这种情况,您只需要在调用SetGridDataSource之前增强逻辑并检查表单是否已关闭或处理。

也就是说,您的设计似乎存在一些问题。

  1. 您正在从线程池调用SetGridDataSource,但它可能应该从UI线程调用。
  2. 你没有正确地链接异步调用。具体而言,您没有在第二个EndInvoke上致电BeginInvoke
  3. 一种可以帮助您正确处理线程的好方法是使用类似BackgroundWorker的内容而不是链接BeginInvoke来电。
  4. 考虑将业务逻辑(包括线程和异步逻辑)从UI层和表单移开。
  5. 在没有异常类型的情况下执行catch是一个非常糟糕的主意,并通过在不重新抛出的情况下忽略它来处理任何抛出的异常。
  6. 顺便说一句,而不是这样做:

    BeginInvoke(new Action<List<ds>>(SetGridDataSource), dataSource);
    

    您可以执行以下操作,我认为这些内容更具可读性:

    BeginInvoke(SetGridDataSource, dataSource);