提问人:Greg Beech 提问时间:10/10/2008 最后编辑:Kevin FairchildGreg Beech 更新时间:11/15/2008 访问量:1703
使用部分类型信息结束异步委托调用
Ending asynchronous delegate invocation with partial type information
问:
使用 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);
}
这非常有效,因为它知道委托的类型是什么,因此可以强制转换。但是,当您有两个方法时,它开始变得混乱,因为尽管该方法返回 void,但您似乎必须将其强制转换为强类型委托才能结束调用,例如Put
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
关于如何在没有级联检查的情况下结束调用的任何想法?is
答:
为什么不通过回到更一般的重载来避免这个问题:
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);
}
评论
编辑:请看我的其他答案。它可以更干净。
我认为没有办法让它更干净。实质上,通过使用相同的方法结束多种类型的异步调用,使这种情况不可避免。您也可以遵循正常模式,将 和 委托作为最后两个参数传递给 (callback 和 asyncstate),并且您仍然必须测试委托在 order call 中的类型。EndPut
EndPut
put
BeginInvoke()
EndInvoke()
把它们投射到根本无济于事。Delegate
我喜欢马克·布兰克特(Mark Brackett)的想法。我认为这归结为以下选项:
- 通过让一个重载调用另一个重载来完全加入它们。一个委托,一个回调。
- 通过两个回调来调用它们,将它们完全分开。
EndInvoke()
除此之外,唯一要做的就是让你的代码更干净一点,并使用 or 查找字典 ,将委托作为该状态对象传递。switch
asyncResult.AsyncState.GetType()
put
评论
我错了,有一种更干净的方法。
在已知委托的特定类型的同一上下文中为特定方法创建委托,并将其作为 AsyncState 传递。为了方便起见,我将其作为回调传递。Action( IAsyncResult )
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 );
}
评论