4

当使用 BeginInvoke/EndInvoke 模式编写异步方法实现时,代码可能如下所示(为了避免您猜测这是一个围绕缓存的异步包装器):

IAsyncResult BeginPut(string key, object value)
{
    Action<string, object> put = this.cache.Put;
    return put.BeginInvoke(key, value, null, null);
}

void EndPut(IAsyncResult asyncResult)
{
    var put = (Action<string, object>)((AsyncResult)asyncResult).AsyncDelegate;
    put.EndInvoke(asyncResult);
}

这非常有效,因为它知道委托的类型是什么,因此可以强制转换。但是,当您有两个方法时,它开始变得混乱Put,因为尽管该方法返回 void 您似乎必须将其强制转换为强类型委托以结束调用,例如

IAsyncResult BeginPut(string key, object value)
{
    Action<string, object> put = this.cache.Put;
    return put.BeginInvoke(key, value, null, null);
}

IAsyncResult BeginPut(string region, string key, object value)
{
    Action<string, string, object> put = this.cache.Put;
    return put.BeginInvoke(region, key, value, null, null);
}

void EndPut(IAsyncResult asyncResult)
{
    var put = ((AsyncResult)asyncResult).AsyncDelegate;

    var put1 = put as Action<string, object>;
    if (put1 != null) 
    {
        put1.EndInvoke(asyncResult);
        return;
    }

    var put2 = put as Action<string, string, object>;
    if (put2 != null) 
    {
        put2.EndInvoke(asyncResult);
        return;
    }

    throw new ArgumentException("Invalid async result", "asyncResult");
}

我希望有一种更简洁的方法来做到这一点,因为我唯一关心委托的是返回类型(在本例中为 void),而不是提供给它的参数。但我绞尽脑汁问办公室里的其他人,谁也想不出答案。

我知道一个解决方案是编写一个 custom IAsyncResult,但这是一项艰巨的任务,因为潜在的线程问题围绕着诸如延迟实例化之类的事情WaitHandle,我宁愿拥有这个看起来有点 hacky 的代码而不是沿着这条路线走。

关于如何在没有级联is检查的情况下结束调用的任何想法?

4

3 回答 3

4

我错了,有更清洁的方法。

在您已经知道委托的特定类型的同一上下文中Action( IAsyncResult )为特定方法创建委托,并将其作为 AsyncState 传递。EndInvoke()为方便起见,我EndPut()作为回调传递。

IAsyncResult BeginPut( string key, object value ) {
    Action<string, object> put = this.Put;
    return put.BeginInvoke( key, value, EndPut,
        new Action<IAsyncResult>( put.EndInvoke ) );
}

IAsyncResult BeginPut( string region, string key, object value ) {
    Action<string, string, object> put = this.Put;
    return put.BeginInvoke( region, key, value, EndPut,
        new Action<IAsyncResult>( put.EndInvoke ) );
}

然后你完成它。

void EndPut( IAsyncResult asyncResult ) {
    var del = asyncResult.AsyncState as Action<IAsyncResult>;
    del( asyncResult );
}
于 2008-10-09T21:42:08.603 回答
0

为什么不通过回到更一般的重载来避免这个问题:

IAsyncResult BeginPut(string key, object value) {
   return this.BeginPut(null, key, value);
}

IAsyncResult BeginPut(string region, string key, object value) {
   Action<string, string, object> put = this.Put;
   return put.BeginInvoke(region, key, value, null, null);
}

void EndPut(IAsyncResult asyncResult) {
   var put = (Action<string, string, object>)((AsyncResult)asyncResult).AsyncDelegate;
   put.EndInvoke(asyncResult);
}
于 2008-10-09T21:22:04.353 回答
0

编辑:请参阅我的其他答案。它可以更清洁。

我不认为有办法让这个更清洁。通过使用相同的EndPut方法来结束不止一种类型的异步调用,您基本上使这不可避免。您也可以通过EndPutput委托作为最后两个参数传递给BeginInvoke()(回调和异步状态)来遵循正常模式,并且您仍然必须测试委托是什么类型才能调用EndInvoke()

将它们投射到Delegate根本没有帮助。

我喜欢 Mark Brackett 的想法。我认为这归结为以下选项:

  • 通过让一个重载呼叫另一个来完全加入他们。一个委托,一个回调。
  • 通过调用两个回调将它们完全分开EndInvoke()

除此之外,唯一的事情就是让你的代码更简洁一些,并使用一个switch或查找字典 using asyncResult.AsyncState.GetType(),将put委托作为该状态对象传递。

于 2008-10-09T21:31:37.593 回答