visual usuario una try tipos sharp que programacion por más manejo existen excepción excepciones específicos errores ejemplos definidas comunes como catch capturar c# .net exception-handling design-patterns

c# - usuario - tipos específicos de excepciones más comunes en programacion



Patrón de comando asíncrono: manejo de excepciones (5)

Lanzaría una excepción personalizada y no llamaría a la devolución de llamada completada. Después de todo, el comando no se completó si se produjo una excepción.

Estoy implementando un patrón de comando asíncrono para la clase "cliente" en una aplicación cliente / servidor. He hecho algunas codificaciones de socket en el pasado y me gusta el nuevo patrón Async que usaron en las clases Socket / SocketAsyncEventArgs.

Mi método asíncrono se ve así: public bool ExecuteAsync(Command cmd); Devuelve verdadero si la ejecución está pendiente y falso si se completó sincrónicamente. Mi pregunta es: ¿Debería llamar siempre a la devolución de llamada (cmd.OnCompleted), incluso en el caso de una excepción? ¿O debería lanzar excepciones directamente desde ExecuteAsync?

Aquí hay más detalles si los necesita. Esto es similar al uso de SocketAsyncEventArgs, pero en lugar de SocketAsyncEventArgs mi clase se llama SomeCmd.

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

Al igual que con la clase Socket, si necesita coordinar con su método de devolución de llamada (SomeCmd_OnCompleted en este caso), puede usar el valor de retorno de ExecuteAsync para saber si la operación está pendiente (verdadera) o si la operación se completó sincrónicamente.

SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!"); cmd.OnCompleted += this.SomeCmd_OnCompleted; if( this.ConnectionToServer.ExecuteAsync(cmd) ) { Monitor.Wait( this.WillBePulsedBy_SomeCmd_OnCompleted ); }

Aquí hay una versión muy simplificada de mis clases base, pero puede ver cómo funciona:

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


Manejar excepciones en un solo lugar es mucho más fácil. Usaría la siguiente distinción: para las excepciones que deberían manejarse, tírelas a la devolución de llamada. Hace que sea más simple usar la clase. Para las excepciones que no se deben capturar (p. Ej., ArgumentException), ejecute ExecuteAsync. Queremos que las excepciones no controladas exploten lo antes posible.


No lanzaría una excepción en ExecuteAsync y, en su lugar, establecería la condición de excepción para la devolución de llamada. Esto creará una forma consistente de programación contra la lógica asíncrona y reducirá el código repetitivo. El cliente puede llamar a esta clase y esperar una forma de manejar las excepciones. Esto proporcionará menos errores, menos código frágil.


Un patrón general para operaciones asincrónicas en .NET (al menos para los pares de métodos BackgroundWorker y BeginInvoke()/EndInvoke() es tener un objeto de resultado que separa la devolución de llamada del valor de retorno real o cualquier excepción que se haya producido. Es responsabilidad del de la devolución de llamada para manejar la excepción.

Algunos pseudocódigos tipo 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 } }

Entonces, si llama a ExecuteCommandAsync() , regresa inmediatamente. BeginExecuteCommand() se inicia en un hilo separado. Si arroja una excepción, esa excepción no se lanzará hasta que llame a EndInvoke() en IAsyncResult (que puede convertir a AsyncResult , que está documentado pero podría pasarlo en el estado si el lanzamiento lo hace sentir incómodo). , el código de manejo de excepciones se ubica "naturalmente" alrededor de donde interactuarías con el valor de retorno del método.

Para obtener más información, obtenga más información sobre el patrón IAsyncResult en MSDN .

Espero que esto ayude.


lanzar una excepción desde el punto de envío puede o no ser útil

llamar a la devolución de llamada que pasa un argumento de excepción requiere la devolución de llamada de finalización para hacer 2 cosas distintas

una segunda devolución de llamada para informes de excepción podría tener sentido