我正在为客户端/服务器应用程序中的“client”类实现异步命令模式。我以前做过一些套接字编码,我喜欢他们在Socket / SocketAsyncEventArgs类中使用的新的异步模式。
我的异步方法如下所示:public bool ExecuteAsync(Command cmd);
如果执行挂起则返回true,如果同步完成则返回false。 我的问题是:即使出现异常,我是否应该始终调用回调(cmd.OnCompleted)?或者我应该从ExecuteAsync中抛出异常吗?
如果您需要,可以在下面找到更多细节。这类似于使用SocketAsyncEventArgs,但是我的类被称为SomeCmd而不是SocketAsyncEventArgs。
SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!");
cmd.OnCompleted += this.SomeCmd_OnCompleted;
this.ConnectionToServer.ExecuteAsync(cmd);
与Socket类一样,如果需要与回调方法(在本例中为SomeCmd_OnCompleted)进行协调,则可以使用ExecuteAsync的返回值来了解操作是否挂起(true)或操作是否同步完成。
SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!");
cmd.OnCompleted += this.SomeCmd_OnCompleted;
if( this.ConnectionToServer.ExecuteAsync(cmd) )
{
Monitor.Wait( this.WillBePulsedBy_SomeCmd_OnCompleted );
}
这是我的基类的大大简化版本,但您可以看到它的工作原理:
class Connection
{
public bool ExecuteAsync(Command cmd)
{
/// CONSIDER: If you don't catch every exception here
/// then every caller of this method must have 2 sets of
/// exception handling:
/// One in the handler of Command.OnCompleted and one where ExecuteAsync
/// is called.
try
{
/// Some possible exceptions here:
/// 1) remote is disposed. happens when the other side disconnects (WCF).
/// 2) I do something wrong in TrackCommand (a bug that I want to fix!)
this.TrackCommand(cmd);
remote.ServerExecuteAsync( cmd.GetRequest() );
return true;
}
catch(Exception ex)
{
/// Command completing synchronously.
cmd.Completed(ex, true);
return false;
}
}
/// <summary>This is what gets called by some magic when the server returns a response.</summary>
internal CommandExecuteReturn(CommandResponse response)
{
Command cmd = this.GetTrackedCommand(response.RequestId);
/// Command completing asynchronously.
cmd.Completed(response, false);
}
private IServer remote;
}
abstract class Command: EventArgs
{
internal void Completed(Exception ex, bool synchronously)
{
this.Exception = ex;
this.CompletedSynchronously = synchronously;
if( this.OnCompleted != null )
{
this.OnCompleted(this);
}
}
internal void Completed(CommandResponse response, bool synchronously)
{
this.Response = response;
this.Completed(response.ExceptionFromServer, synchronously)
}
public bool CompletedSynchronously{ get; private set; }
public event EventHandler<Command> OnCompleted;
public Exception Exception{ get; private set; }
internal protected abstract CommandRequest GetRequest();
}
答案 0 :(得分:5)
.NET中异步操作的一个通用模式(至少对于BackgroundWorker
和BeginInvoke()/EndInvoke()
方法对是一个结果对象,它将回调与实际返回值或任何异常分开发生了。回调的责任是处理异常。
一些类似C#的伪代码:
private delegate int CommandDelegate(string number);
private void ExecuteCommandAsync()
{
CommandDelegate del = new CommandDelegate(BeginExecuteCommand);
del.BeginInvoke("four", new AsyncCallback(EndExecuteCommand), null);
}
private int BeginExecuteCommand(string number)
{
if (number == "five")
{
return 5;
}
else
{
throw new InvalidOperationException("I only understand the number five!");
}
}
private void EndExecuteCommand(IAsyncResult result)
{
CommandDelegate del;
int retVal;
del = (CommandDelegate)((AsyncResult)result).AsyncDelegate;
try
{
// Here's where we get the return value
retVal = del.EndInvoke(result);
}
catch (InvalidOperationException e)
{
// See, we had EndExecuteCommand called, but the exception
// from the Begin method got tossed here
}
}
因此,如果您致电ExecuteCommandAsync()
,则会立即返回。 BeginExecuteCommand()
在单独的线程中启动。如果它抛出一个异常,那么在你EndInvoke()
上调用IAsyncResult
之前就不会抛出该异常(你可以将其转换为AsyncResult
,但是你可以将其传递给{{1}}。状态如果演员表让你感到不舒服。这样,异常处理代码“自然地”放在你将与方法的返回值进行交互的地方。
有关详细信息,请查看有关the IAsyncResult pattern on MSDN的更多信息。
希望这有帮助。
答案 1 :(得分:4)
我会不在ExecuteAsync中抛出异常,而是为回调设置异常条件。这将创建一种针对异步逻辑的一致编程方式,并减少重复代码。客户端可以调用此类并期望一种方法来处理异常。这将提供更少的错误,更少的脆弱代码。
答案 2 :(得分:3)
从发送点抛出异常可能有用也可能没用
调用回调传递异常参数需要完成回调才能完成2个不同的事情
异常报告的第二个回调可能有意义
答案 3 :(得分:1)
我会抛出一个自定义异常,而不是调用已完成的回调。毕竟,如果发生异常,命令就没有完成。
答案 4 :(得分:0)
在一个地方处理异常要容易得多。我将使用以下区别:对于应该处理的异常,将它们放入回调中。它使得使用该类更简单。对于不应被捕获的异常(例如,ArgumentException),请执行ExecuteAsync。我们希望未处理的例外情况尽快爆发。