2010-06-17 13 views
7

Trabajar con .NET framework Tengo un servicio con un conjunto de métodos que puede generar varios tipos de excepciones: MyException2, MyExc1, Exception ... Para proporcionar el trabajo adecuado para todos métodos, cada uno de ellos contiene los siguientes apartados:Cómo evitar excepciones atrapa copiar y pegar en .NET

[WebMethod] 
void Method1(...) 
{ 
    try 
    { 
     ... required functionality 
    } 
    catch(MyException2 exc) 
    { 
     ... process exception of MyException2 type 
    } 
    catch(MyExc1 exc) 
    { 
     ... process exception of MyExc1 type 
    } 
    catch(Exception exc) 
    { 
     ... process exception of Exception type 
    } 
    ... process and return result if necessary 
} 

es muy aburrido tener exactamente el mismo material en cada método de servicio (cada método tiene un conjunto diferente de parámetros) con exactamente mismas excepciones funcionalidad de procesamiento ...

¿Hay alguna posibilidad de "agrupar" estas secciones de captura y usar solo una línea (algo similar a las macros de C++)? Probablemente algo nuevo en .NET 4.0 esté relacionado con este tema?

Gracias.

P.S. Cualquier pensamiento es bienvenido

Respuesta

6

Si el manejo de excepciones es exactamente el mismo en todas sus métodos, se podría hacer algo como:

void CallService(Action method) 
{ 
    try 
    { 
     // Execute method 
     method(); 
    } 
    catch(MyException2 exc) 
    { 
     ... process exception of MyException2 type 
    } 
    catch(MyExc1 exc) 
    { 
     ... process exception of MyExc1 type 
    } 
    catch(Exception exc) 
    { 
     ... process exception of Exception type 
    } 
} 

Entonces, sólo podría reescribir su código de cliente para hacer:

int i = 3; 
string arg = "Foo"; 
this.CallService(() => this.Method1(i)); 
this.CallService(() => this.Method2(arg, 5)); 

Esto permite que sus métodos Method1 y Method2 sean simplemente:

void Method1(int arg) 
{ 
    // Leave out exception handling here... 
    ... required functionality 
    ... process and return result if necessary 
} 

void Method2(string stringArg, int intArg) 
{ 
    // Leave out exception handling here... 
    ... required functionality 
    ... process and return result if necessary 
} 
+0

Tuve una idea similar, pero el problema es que cada método tiene un conjunto diferente de parámetros (lo siento, no mencioné eso en la pregunta original y la especificación añadida recientemente). Aquí hay un ejemplo de firmas de métodos: cadena Método1 (cadena); void Método2 (cadena, int, int), etc ... – Budda

+0

@Budda: eso no importa - va a funcionar bien con eso, ya que lambdas puede pasar todos los argumentos, la forma en que lo escribí. –

+0

@Budda: Edité mi respuesta para demostrar :) Esto funciona para cualquier cantidad de argumentos en los métodos ... –

-3

Sé que es una mala práctica, pero si tiene exactamente el mismo tratamiento de errores en cada instrucción catch, ¿por qué no usar la última instrucción catch como un catch all?

Esto, por supuesto, asume que todas sus excepciones heredan de Exception.

+0

-1: si sabes que es una mala práctica, ¿por qué lo recomiendas? –

+0

No lo soy, solo tengo curiosidad por saber por qué tendrías exactamente el mismo error de manejo en múltiples declaraciones catch, ya que el punto de tener múltiples capturas es que manejas tus excepciones de manera diferente ... –

+0

Tengo exactamente el mismo manejo para proporcionar el mismo procesamiento de errores para todos los métodos. – Budda

5

¿Por qué no simplemente factorizar el código en un método auxiliar para hacerlo por usted (puede agregar las nuevas excepciones que necesite en el futuro al HandleException también, lo que lo hace bastante escalable)?

try 
{ 
    ... required functionality 
} 
catch (Exception e) 
{ 
    HandleException(e); 
    throw; // only if you need the exception to propagate to caller 
} 


private void HandleException(Exception e) 
{ 
    if (e is MyException2) 
    { 
     ... process exception of MyException2 type 
    } 
    else if (e is MyExc1) 
    { 
     ... process exception of MyExc1 type 
    } 
    else 
    { 
     ... process exception of Exception type 
    } 
} 
+0

dcp, ¿por qué vuelve a lanzar la excepción después de llamar a HandleException (e)? – Budda

+1

Este patrón incumpliría definitivamente la regla de análisis de código de Microsoft "No atrape los tipos de excepción generales" http://msdn.microsoft.com/en-us/library/ms182137(v=VS.90).aspx – chilltemp

+3

@chilltemp - Vamos , el no repetir tu auto-regla tiene una precedencia mucho más alta. – ChaosPandion

2

Me gustaría ver de cerca lo que está haciendo para "procesar" estas excepciones. Existe una buena posibilidad de que no necesites los bloques catch, y que deberías permitir que las excepciones se propaguen.

+0

Esos métodos son [WebMethod] del servicio web y SIEMPRE deben devolver algo significativo al cliente. Todas las secciones de manejo de excepciones generan objetos de "error", que se serializan en formato XML y se envían al cliente. Su sugerencia me dio una idea para optimizar la generación de objetos de error ... se puede implementar con un patrón de fábrica ... de la siguiente manera: catch (Exception exc) { MyError error = MyError.GenerateErrorObject (exc); ... proceso (error); } Gracias. – Budda

+0

@Budda: 1) Eso es bueno saberlo. Dígalo la próxima vez. 2) Debería lanzar 'SoapException' con el conjunto de detalles para devolver un SOAP Fault. –

+0

Necesito enviar el mensaje en CIERTO formato personalizado, y la excepción SOAP no cabe :) – Budda

1

En un repentino destello de geek (y para mostrar lo que podría, pero probablemente no debería y no quiere hacer): Puede hacer que todo sea más composable y reutilizable generando funciones sobre la marcha que contengan la excepción lógica de manejo:

static class ExceptionHandlerExtensionMethods 
{ 
    // extend to Func<T> as desired 
    public static Action Catching<T>(this Action what, Action<T> handler) 
     where T : Exception 
    { 
     return() => 
     { 
      try 
      { 
       what(); 
      } 
      catch (T ex) 
      { 
       handler(ex); 
      } 
     }; 
    } 
} 

ya se puede poner en práctica y funciones de controlador específicas reutilización de tipo excepción alguna parte, y componerlos en el manejo de excepciones de algún otro método.

Para eliminar la redundancia, puede escribir una función auxiliar que agregue las funciones del manejador de excepciones "típicas" a lo que quiera llamar y llamar a este método decorado en su lugar.

0

Utilizando el método de Reed Copsey CallService:

void DoSomething(object param1, int param2) 
{ 
    this.CallService(() => 
    { 
     // work with param1 and param2 here 
    } 
} 

Para los casos en los que hay que devolver un valor, es posible que tenga que duplicar CallService para devolver un parámetro de tipo.

T CallService<T>(Func<T> callback) { /* ... */ } 
1

He hecho lo siguiente en una situación similar. Voy a mostrar la técnica en dos pasos ...


Paso 1. Crear un método que proporciona un contexto de ejecución específico para otro código:

// this static method is responsible for setting up a context for other code 
// to run in; specifically, it provides the exception handling "plumbing": 
public static void guarded(Action action) 
{      // ^^^^^^^^^^^^^ 
    try    // actual code to be executed 
    { 
     action(); 
    } 
    catch (SomeExceptionA ea) 
    { 
     // handle exception... 
    } 
    catch (SomeExceptionB eb) 
    { 
     // handle exception... 
    } 
    // etc. 
} 

Paso 2 Aplique este contexto a cualquier fragmento de código:

A continuación, simplemente "ajusta" este manejo de excepciones alrededor del acto código UAL en sus métodos:

public void MethodA() 
{ 
    guarded(() => // <-- wrap the execution handlers around the following code: 
    { 
     // do something which might throw an exception... 
    }); 
} 

public void MethodB() 
{ 
    guarded(() => 
    { 
     // do something which might throw an exception... 
    }); 
} 

Resumen:

La idea general es escribir una función (guarded en el ejemplo anterior) que establece un contexto de ejecución específico para otro código para ejecutar. (El contexto en este ejemplo proporciona manejo de excepciones.) El código que se debe ejecutar en este contexto se proporciona como una función lambda. Incluso podría adaptar la función de creación de contexto para que el código en la función lambda pueda devolver un valor.

+0

Igual que Reed Copsey sugirió. Pero gracias de todas maneras. – Budda

+1

Vaya. Perdón por el casi duplicado, de alguna manera pude pasar por alto su respuesta. ** PERO ** note una sutil diferencia en nuestras dos respuestas: en su respuesta, la función externa está explícitamente envuelta alrededor del método real _por la llamada del método_. Sugeriría que también hay casos en los que es más apropiado que sus métodos manejen su contexto ellos mismos. Podrían saber mejor que la persona que llama qué excepciones pueden/deben ser manejadas. – stakx

Cuestiones relacionadas