使用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
检查的情况下结束调用的任何想法?
答案 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
方法结束多种类型的异步调用来实现这一点。您也可以通过将EndPut
和put
委托作为最后两个参数传递给BeginInvoke()
(回调和异步状态)来遵循正常模式,您仍然需要测试什么键入委托是为了调用EndInvoke()
。
将它们投射到Delegate
并没有任何帮助。
我喜欢Mark Brackett的想法。我认为归结为这些选择:
EndInvoke()
两个回调来完全分开它们。除此之外,唯一的办法是让你的代码更清洁,并使用switch
或使用asyncResult.AsyncState.GetType()
查找字典,并将put
委托作为该状态对象传递。