2009-11-25 4 views
6

Digamos que tengo una función que necesita para devolver algún valor entero. pero también puede fallar, y necesito saber cuándo lo hace.que es mejor, usando un parámetro que arroja valores de nulo o de retorno booleano

¿Cuál es la mejor manera?

public int? DoSomethingWonderful() 

o

public bool DoSomethingWonderful(out int parameter) 

esto es probablemente más una cuestión de estilo, pero todavía estoy curioso lo que la gente se tome la opción.

Editar: aclaración, este código se refiere a una caja negra (vamos a llamarlo una nube .no, una caja negra. No, espera. Nube, sí). No me importa por qué falló. Solo necesitaría saber si tengo un valor válido o no.

Respuesta

12

me gusta la versión anulable mejor, porque se puede utilizar el operador nulo coalesce? en él, por ejemplo:

int reallyTerrible = 0; 
var mightBeWonderful = DoSomethingWonderful() ?? reallyTerrible; 
+2

Justo lo que iba a escribir. También significa que no necesita declarar una variable separada si no la necesita. Básicamente, permite más expresiones fluidas. –

+4

¡GUAU! ¡Acabo de adelantar a Jon Skeet en una respuesta SO! ¡No podré dormir esta noche! – Manu

+0

Esto es realmente bueno. Depende de cómo debe manejarse este nulo. Debido a que "también puede fallar" en la pregunta, asumí que es un error y no debería ser ignorado así. Si puede, este enfoque es definitivamente el mejor. –

2

Me gustaría seguir el patrón utilizado en algún lugar en la biblioteca .Net como:

bool int.TryParse(string s, out value) 
bool Dictionary.TryGetValue(T1 key, out T2 value) 

Así que diría:

public bool TryDoSomethingWonderful(out int parameter) 
0

Si sólo hay una forma en que puede fallar, o si nunca necesitará saber qué falló, yo diría que es probable que sea más simple y más fácil de ir con la anulable valor de retorno

Por el contrario, si hay varias maneras en que podría fallar, y el código de llamada podría querer saber exactamente por qué falló, entonces vaya con el parámetro out y devuelva un código de error en lugar de un bool (alternativamente, podría lanzar un excepción, pero en función de su pregunta, parece que ya ha decidido no lanzar una excepción).

2

me gustaría utilizar la segunda, debido, probablemente, lo que necesito saber de inmediato si la llamada se realizó correctamente, y en ese caso yo preferiría escribir

int x; 
if(DoSomethingWonderful(out x)) 
{ 
    SomethingElse(x); 
} 

que

int? x = DoSomethingWonderful(); 
if(x.HasValue) 
{ 
    SomethingElse(x.Value); 
} 
+0

Para hacer que sus fragmentos de código sean comparables, también debe declarar x en el primer fragmento. –

+0

En realidad, necesitaría declarar la variable x antes de usarla en el primer ejemplo, por lo que la cantidad de líneas de código será la misma. –

+0

Bueno, eso podría ser porque estás comparando con un uso extraño. ¿Qué tal 'if (x! = Null) {SomethingElse ((int) x); } '? – Joren

2

Realmente depende de qué estás haciendo.

¿Es nula una respuesta significativa? Si no, preferiría una llamada al método bool TryDoSomethingWonderful(out int). Esto coincide con el Marco.

Si, sin embargo, nulo es un valor de retorno significativo, ¿se devuelve int? tiene sentido.

0

Más bien debería usar un try catch. ¿Parece que la persona que llama no sabe lo que podría pasar?

¿Deberíamos verificar tanto el bool como el out, o debería verificar tanto el retorno nulo como el retorno real?

Deje que el método haga lo que debería, y en el caso de que falle, informe a la persona que llama que falló, y que la persona que llama lo requirió.

5

Depende de cómo piense que debería ser el código de llamada. Y, por lo tanto, para qué se utiliza su función.

En general, debe evitar los argumentos. Por otro lado, podría ser bueno tener un código como éste:

int parameter; 
if (DoSomething(out paramameter)) 
{ 
    // use parameter 
} 

Cuando se tiene una int anulable, se vería así:

int? result = DoSomething(); 
if (result != null) 
{ 
    // use result 
} 

Esto es algo mejor porque usted don' Tiene un argumento de salida, pero el código que decide si la función tuvo éxito no parece muy obvio.

No olvide que hay otra opción: use Exeptions. Solo haga esto si el caso en el que falla su función es realmente un caso de error excepcional.

try 
{ 
    // normal case 
    int result = DoSomething() 
} 
catch (SomethingFailedException ex) 
{ 
    // exceptional case 
} 

Una ventaja de la excepción es que no puede simplemente ignorarla. El caso normal también es sencillo de implementar. Si el caso excepcional fuera algo que pudieras ignorar, no deberías usar excepciones.

Editar: se olvidó de mencionar: otra ventaja de una excepción es que también puede proporcionar información por qué la operación ha fallado. Esta información es proporcionada por el tipo de Excepción, las propiedades de la Excepción y el texto del mensaje.

+0

He crecido para tratar de evitar excepciones. Trabajo en la automatización de pruebas y una excepción accidentalmente no controlada por otro ingeniero puede provocar un desastre. todo el mundo prueba para obtener una devolución válida, pero la gente a menudo olvida capturar :( –

+0

Probablemente no necesites el parámetro 'out' en el caso' int? 'en tu ejemplo; supongo que el valor entregado en' out' parámetro en el primer ejemplo es lo que se devuelve en el segundo. –

+0

@Oren: Pero si olvida probar los códigos de error, se ignora un posible problema. Las excepciones le permiten manejar el problema en un nivel superior, por ejemplo, cerrar una ventana, pero no el cliente o lo que sea. Yo solía trabajar con código que tiene códigos de error, y es lo que llamo un desastre ... estoy hablando de errores, no de "solo-sin-valor" -casos. –

3

¿Por qué no lanzar una excepción?

+4

No use excepciones si no es REALMENTE excepcional. – Manu

+1

Pero no sabe si es excepcional o no, solo lo hace OP. Quizás el OP debería sonar; ¿Esperas que tu método falle en circunstancias normales? Si no, la excepción es el camino a seguir. –

+1

Cito las pautas de diseño de .NET: "Las excepciones son el mecanismo estándar para informar errores. Las aplicaciones y las bibliotecas no deben usar códigos de retorno para comunicar errores". http://msdn.microsoft.com/en-us/library/ms229014(VS.80).aspx – Mathias

2

A menos que el rendimiento sea la principal preocupación, debe devolver un int y lanzar una excepción en caso de error.

1

Estoy a favor de usar un parámetro de salida. En mi opinión, este es el tipo de situación para la cual el uso de los parámetros de salida es más adecuado.

Sí, puede usar el operador de fusión para mantener su código como un trazador de líneas si y solo si tiene un valor alternativo que puede usar en el resto de su código. A menudo me parece que no es el caso para mí, y preferiría ejecutar una ruta de código diferente de la que tendría si pudiera recuperar un valor.

 int value; 
     if(DoSomethingWonderful(out value)) 
     { 
      // continue on your merry way 
     } 
     else 
     { 
      // oops 
      Log("Unable to do something wonderful"); 

      if (DoSomethingTerrible(out value)) 
      { 
       // continue on your not-so-merry way 
      } 
      else 
      { 
       GiveUp(); 
      } 
     } 

Además, si el valor que quiero recuperar en realidad es anulable, a continuación, utilizando una función con un parámetro de salida y un valor de retorno booleano es, en mi opinión, la forma más fácil de decir la diferencia entre "yo no fue exitoso al recuperar el valor "y" El valor que recuperé es nulo ". A veces me importa esa distinción, como en el siguiente ejemplo:

private int? _Value; 
    private bool _ValueCanBeUsed = false; 

    public int? Value 
    { 
     get { return this._Value; } 
     set 
     { 
      this._Value = value; 
      this._ValueCanBeUsed = true; 
     } 
    } 

    public bool DoSomethingTerrible(out int? value) 
    { 
     if (this._ValueCanBeUsed) 
     { 
      value = this._Value; 
      // prevent others from using this value until it has been set again 
      this._ValueCanBeUsed = false; 
      return true; 
     } 
     else 
     { 
      value = null; 
      return false; 
     } 
    } 

En mi opinión, la única razón por la mayoría de la gente tiende a no utilizar los parámetros de salida es porque encuentran la sintaxis engorroso. Sin embargo, realmente siento que el uso de parámetros de salida es la solución más adecuada para este problema, y ​​descubrí que una vez que me acostumbré a él, encontré que la sintaxis es mucho más preferible que devolver un valor nulo.

0

Curiosamente, mi opinión personal se basa significativamente en la naturaleza del método. Específicamente, si el propósito del método es , recupere un solo valor, como opuesto a "hacer algo".

Ex:

bool GetSerialNumber(out string serialNumber) 

vs

string GetSerialNumber() // returns null on failure 

El segundo se siente más "natural" para mí de alguna manera, y del mismo modo:

bool GetDeviceId(out int id) 

vs

int? GetDeviceId() // returns null on failure` 

Pero admito que esto realmente cae en el territorio del "estilo de codificación".

Ah, y yo también tendería a favorecer a lanzar una excepción:

int GetDeviceId() // throws an exception on read failure 

todavía no estoy vendido en qué estarían tan mal. ¿Podemos tener un hilo sobre eso, Oren? ;-)

+0

http://stackoverflow.com/questions/1744070/why-should-exceptions-be-used-conservatively –

0

No me gusta el patrón "Try" de Microsoft en el que el parámetro "out" se utiliza para devolver un elemento de datos. Entre otras cosas, los métodos codificados de esa manera no se pueden usar en interfaces covariantes. Preferiría ver un método codificado como: T GetValue(out bool Successful) o quizás T GetValue(out GetValueErrorEnum result); o T GetValue(out GetValueErrorInfo result); si fuera necesario algo más que verdadero/falso. Como cada tipo de datos tiene un valor predeterminado legal, no hay problema para decidir qué devolver si falla la función. Código de llamada puede decir fácilmente:

 
    bool success; 
    var myValue = Thing.GetValue(ref success); 
    if (success) 
    .. do something using myValue 
    else 
    .. ignore myValue 

Sería bueno si .NET y C# ofreció cierto covariante 'copiar hacia fuera' parámetros (la persona que llama que asignar espacio para el resultado, pasa un puntero a ese espacio a la función llamada , y luego copie el espacio asignado a la variable pasada solo después de que se devuelva la función).

Cuestiones relacionadas