2010-10-08 19 views
15

que tienen un método como ...declara que un método siempre arroja una excepción?

int f() { 
    try { 
    int i = process(); 
    return i; 
    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
} 

Esto produce un error de compilación, "no todas las rutas de código devuelven un valor". Pero en mi caso ThrowSpecificFault() arrojará siempre (la apropiada) excepción. Entonces me veo forzado a poner un valor de retorno al final, pero esto es feo. El propósito de este patrón en primer lugar es porque "proceso()" es una llamada a un servicio web externo, pero necesita traducir una variedad de excepciones diferentes para que coincidan con la interfaz esperada de un cliente (~ patrón de fachada, supongo) .

¿Alguna manera más limpia de hacer esto?

+1

Relacionado: [¿Existe un atributo estándar "nunca regresa" para las funciones C#?] (Http://stackoverflow.com/questions/1999181/is-there-a-standard-never-returns-attribute-for-c -funciones) –

Respuesta

47

Sugiero que convierta ThrowSpecificFault(ex)-throw SpecificFault(ex); el método SpecificFault devolvería el objeto de excepción que se lanzará en lugar de tirarlo por sí mismo. Mucho más limpio.

Este es el patrón recomendado por Microsoft's guidelines (busque el texto "Usar métodos de creación de excepciones").

+5

+1, este es el patrón recomendado en las directrices de Microsoft. – Joe

+0

¿Tiene un enlace? – noctonura

+2

Lo hago: http://msdn.microsoft.com/en-us/library/seyhszts.aspx; encuentre el texto "Usar métodos de creación de excepciones". – CesarGon

3

No.

Imagínese si ThrowSpecificFault se definieron en un DLL separada. Si modifica la DLL para no lanzar una excepción, luego ejecute su programa sin recompilarlo, ¿qué pasaría?

+3

Imagine si el método Foo se definió en una DLL separada.Si modifica Foo para devolver un largo en vez de un int, entonces ejecute su programa que llama a Foo sin recompilarlo, ¿qué pasaría? *Nada bueno*. * * Nunca * es correcto cambiar la firma de un método en una biblioteca externa y luego seguir utilizándolo sin volver a compilar. Es por eso que tenemos sellos de versión, etc. en ensamblajes. –

+0

@Eric - Creo que la situación hipotética de SLaks no requiere cambiar la firma, por lo que no es un cambio radical. – kvb

+2

@kvb: La característica propuesta, según tengo entendido, es capturar el hecho de que un método nunca regresa * en su firma *. –

8

El problema aquí, es que si entra en el bloque catch en f() su función nunca devolverá un valor. Esto dará como resultado un error porque declaró su función como int, lo que significa que le dijo al compilador que su método devolverá un número entero.

El siguiente código hará lo que está buscando y siempre devolverá un número entero.

int f() { 
    int i = 0; 
    try { 
    i = process(); 

    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
    return i; 
} 

ponga la declaración de devolución al final de su función y todo irá bien.

Siempre es una buena idea asegurarse de que su método siempre devuelva un valor independientemente de la ruta de ejecución de la aplicación.

+0

+1 ¡¡¡me has vencido por un minuto !! –

1

¿Qué tal:

int f() { 
int i = -1; 
try { 
    i = process();  
} catch(Exception ex) { 
    ThrowSpecificFault(ex); 
} 
return i; 
} 
+0

Lo dejo como una respuesta, pero Robert Greiner me ganó. –

2

tiene tres opciones:

Siempre vuelva I, pero antes de declarar que:

int f() { 
    int i = 0; // or some other meaningful default 
    try { 
     i = process(); 
    } catch(Exception ex) { 
     ThrowSpecificFault(ex); 
    } 
    return i; 
} 

Volver a excepción del método y arrojo que:

int f() { 
    try { 
     int i = process(); 
     return i; 
    } catch(Exception ex) { 
     throw GenerateSpecificFaultException(ex); 
    } 
} 

O crea una clase de excepción personalizada y tira eso:

int f() { 
    try { 
     int i = process(); 
     return i; 
    } catch(Exception ex) { 
     throw new SpecificFault(ex); 
    } 
} 
0

Sí.

No espere que ThrowSpecificFault() arroje la excepción.Haga que devuelva la excepción, luego tírela aquí.

En realidad, tiene más sentido también. No utiliza excepciones para el flujo "normal", por lo que si lanza una excepción cada vez, la excepción se convierte en la regla. Crear la excepción específica en la función, y tirarlo aquí porque es una excepción a la corriente aquí ..

0

supongo que se podría hacer ThrowSpecificFault devolver un objeto y, a continuación, usted podría

return ThrowSpecificFault(ex)

De lo contrario, podría reescribir ThrowSpecificFault como un constructor para un subtipo Exception, o podría simplemente hacer ThrowSpecificFault en una fábrica que cree la excepción pero no la arroje.

3

Se puede hacer así:

catch (Exception ex) 
{ 
    Exception e = CreateSpecificFault(ex); 
    throw e; 
} 
0

En caso de que, pero que no es su conocimiento del compilador. Ahora hay forma de decir que este método seguramente arrojará alguna desagradable excepción.

probar este

int f() { 
    try { 
    return process(); 
    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
    return -1; 
} 

También puede utilizar la palabra clave de tiro

int f() { 
    try { 
    return process(); 
    } catch(Exception ex) { 
    throw ThrowSpecificFault(ex); 
    } 
} 

Pero entonces que el método debe devolver alguna excepción en lugar de tirarlo.

0

Use Unity.Interception para limpiar el código. Con el manejo de intercepción, el código podría tener este aspecto:

int f() 
{ 
    // no need to try-catch any more, here or anywhere else... 
    int i = process(); 
    return i; 
} 


Todo lo que necesita hacer en el siguiente paso es definir un controlador de intercepción, que se puede adaptar a medida para el manejo de excepciones. Con este manejador, puede manejar todas las excepciones lanzadas en su aplicación. Lo bueno es que ya no tiene que marcar todo su código con bloques try-catch.

public class MyCallHandler : ICallHandler, IDisposable 
{ 
    public IMethodReturn Invoke(IMethodInvocation input, 
     GetNextHandlerDelegate getNext) 
    { 
     // call the method 
     var methodReturn = getNext().Invoke(input, getNext); 

     // check if an exception was raised. 
     if (methodReturn.Exception != null) 
     { 
      // take the original exception and raise a new (correct) one... 
      CreateSpecificFault(methodReturn.Exception); 

      // set the original exception to null to avoid throwing yet another 
      // exception 
      methodReturn.Exception = null; 
     } 

     // complete the invoke... 
     return methodReturn; 
    } 
} 

El registro de una clase en el controlador se puede realizar mediante un archivo de configuración o programáticamente. El código es bastante directo. Después del registro, se ejemplariza sus objetos utilizando la Unidad, así:

var objectToUse = myUnityContainer.Resolve<MyObjectToUse>(); 

Más sobre Unity.Interception:

http://msdn.microsoft.com/en-us/library/ff646991.aspx

7

En este momento un tipo de retorno puede ser un tipo, o "vacío", que significa "sin tipo de devolución". En teoría, podríamos agregar un segundo tipo de devolución especial "nunca", que tiene la semántica que desea. El punto final de una declaración de expresión que consiste en una llamada a un método de devolución "nunca" se consideraría inalcanzable, por lo que sería legal en cada contexto en C# en el que un "goto", "throw" o "return" es legal .

Es muy poco probable que esto se agregue al sistema de tipos ahora, diez años después. La próxima vez que diseñe un sistema de tipos desde cero, recuerde incluir un tipo "nunca".

Cuestiones relacionadas