2008-11-18 2 views
3

Я реализую асинхронный командный шаблон для класса «клиент» в приложении клиент/сервер. В прошлом я сделал некоторую кодировку сокетов, и мне нравится новый шаблон Async, который они использовали в классах Socket/SocketAsyncEventArgs.Шаблон команды Async - обработка исключений

Мой метод async выглядит следующим образом: public bool ExecuteAsync(Command cmd); Возвращает true, если выполнение выполняется в ожидании и false, если оно выполнено синхронно. Мой вопрос: должен ли я всегда вызывать обратный вызов (cmd.OnCompleted) даже в случае исключения? Или я должен исключать исключения из ExecuteAsync?

Вот еще несколько деталей, если они вам понадобятся. Это похоже на использование SocketAsyncEventArgs, но вместо SocketAsyncEventArgs мой класс называется SomeCmd.

SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!"); 
cmd.OnCompleted += this.SomeCmd_OnCompleted; 
this.ConnectionToServer.ExecuteAsync(cmd); 

Как с классом сокета, если вам необходимо согласовать с вашим методом обратного вызова (SomeCmd_OnCompleted в данном случае), вы можете использовать возвращаемое значение ExecuteAsync знать, если операция находится на рассмотрении (истина) или если операция выполнена синхронно.

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(); 
} 

ответ

3

выбрасыванием исключения из пункта отправки может или не может быть полезным

вызывая функцию обратного вызова, проходящей аргумент исключения требует завершения обратного вызова, чтобы сделать 2 разные вещи

второй обратного вызова для представления исключения может иметь смысл вместо этого

+0

Теперь, почему Я не думаю об этом? Самое смешное, что я на самом деле сделал это раньше для класса, который запускает ряд запросов. – 2008-11-18 03:15:02

1

Я хотел бы бросить специальное исключение, а не называть законченную функцию обратного вызова. В конце концов, команда не была завершена, если произошло исключение.

4

Я бы не вызывать исключение в ExecuteAsync и вместо этого устанавливать условие исключения для обратного вызова. Это создаст последовательный способ программирования против асинхронной логики и сокращения повторяющегося кода. Клиент может вызвать этот класс и ожидать, что один из способов обработки исключений. Это обеспечит меньший багги, менее хрупкий код.

0

Обработка исключений в одном месте намного проще. Я бы использовал следующее различие: для исключений, которые нужно обрабатывать, бросайте их в обратном вызове. Это упрощает использование класса. Для исключений, которые не следует улавливать (например, ArgumentException), вызывать ExecuteAsync. Мы хотим, чтобы необработанные исключения взорвались как можно скорее.

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, который задокументирован, но вы можете передать его в состоянии, если приведение делает вас неудобным. код обработки исключений «естественно, помещается» вокруг, где вы будете взаимодействовать с возвращаемым значением методы.

Для получения дополнительной информации, оформление заказа дополнительной информации о the IAsyncResult pattern on MSDN.

Надеется, что это помогает.

+0

Обратите внимание, что исключение будет выдано только на EndXYZ, не гарантируется даже в коде .NET Framework. Например, вызов BeginXYZ на прокси-сервере службы WCF, указывающий на конечную точку, которая не существует. Исключение выбрасывается немедленно. – 2009-05-29 11:24:07

Смежные вопросы