使用部分类型信息结束异步委托调用

时间:2008-10-09 21:10:14

标签: c# asynchronous delegates

使用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)而不是提供给它的参数。但我绞尽脑汁问办公室里的其他人,没有人能想到答案。

我知道一个解决方案是编写一个自定义IAsyncResult,但这是一个如此艰巨的任务,因为WaitHandle的懒惰实例化之类的潜在线程问题我宁愿这个有点hacky寻找代码而不是走那条路。

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

3 个答案:

答案 0 :(得分:4)

我错了,有一种更清洁的方式。

您在已知道委托的特定类型的相同上下文中为特定Action( IAsyncResult )方法创建EndInvoke()个委托,并将其作为AsyncState传递。为方便起见,我正在传递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 );
}

答案 1 :(得分: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);
}

答案 2 :(得分:0)

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

我认为没有办法让这个更清洁。您基本上通过使用相同的EndPut方法结束多种类型的异步调用来实现这一点。您也可以通过将EndPutput委托作为最后两个参数传递给BeginInvoke()(回调和异步状态)来遵循正常模式,您仍然需要测试什么键入委托是为了调用EndInvoke()

将它们投射到Delegate并没有任何帮助。

我喜欢Mark Brackett的想法。我认为归结为这些选择:

  • 通过另一个重载调用来完全加入它们。一位代表,一位回调。
  • 通过调用EndInvoke()两个回调来完全分开它们。

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