正在结束具有部分类型信息的异步委托调用
本文关键字:信息 异步 调用 类型 结束 | 更新日期: 2023-09-27 17:48:52
使用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");
}
我希望有一种更干净的方法来做这件事,因为我唯一关心的是委托的返回类型(在这种情况下是无效的),而不是提供给它的论点。但我绞尽脑汁,问了办公室里的其他人,没有人能想到答案。
我知道一个解决方案是编写一个自定义的IAsyncResult
,但这是一项非常困难的任务,因为围绕WaitHandle
的懒惰实例化之类的事情存在潜在的线程问题,所以我宁愿有这个看起来有点粗糙的代码,也不愿走这条路。
关于如何在没有级联is
检查的情况下结束调用,有什么想法吗?
我错了,有一种更干净的方法。
您可以在已经知道特定委托类型的同一上下文中为特定EndInvoke()
方法创建Action( IAsyncResult )
委托,并将其作为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 );
}
为什么不回到更通用的过载:来避免这个问题
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);
}
编辑:请查看我的其他答案。它可以更清洁。
我不认为有什么办法可以让这里更干净。通过使用相同的EndPut
方法结束多种类型的异步调用,您基本上使这一点不可避免。您也可以遵循正常模式,将EndPut
和put
委托作为最后两个参数传递给BeginInvoke()
(回调和异步状态),并且您仍然需要测试委托的类型才能调用EndInvoke()
。
将它们选为Delegate
根本没有帮助。
我喜欢Mark Brackett的想法。我认为这可以归结为以下几种选择:
- 通过让一个重载调用另一个来完全加入它们。一个委托,一个回调
- 通过具有两个用于调用
EndInvoke()
的回调来完全分离它们
除此之外,唯一需要做的就是让代码稍微干净一点,并使用switch
或使用asyncResult.AsyncState.GetType()
的查找字典,将put
委托作为状态对象传递。