Pregunta

Digamos que tengo una función que necesita devolver algún valor entero. pero también puede fallar, y necesito saber cuándo lo hace.

¿Cuál es la mejor manera?

public int? DoSomethingWonderful()

o

public bool DoSomethingWonderful(out int parameter)

Probablemente sea más una pregunta de estilo, pero todavía tengo curiosidad por saber qué opción tomaría la gente.

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

¿Fue útil?

Solución

Me gusta más la versión anulable, porque puede usar el operador de fusión nula ?? en él, por ejemplo:

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

Otros consejos

Depende de cómo creas que debe verse el código de llamada. Y, por lo tanto, para qué se usa tu función.

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

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

Cuando tienes un int que puede ser nullable, se vería así:

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

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

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

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

Una de las ventajas de la excepción es que no puedes ignorarla. El caso normal también es sencillo de implementar. Si el caso excepcional es algo que podría ignorar, no debe usar excepciones.

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

¿Por qué no lanzar una excepción?

Seguiría el patrón utilizado en algún lugar de 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)

Realmente depende de lo que estés haciendo.

¿Es nula una respuesta significativa? De lo contrario, 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, ¿devuelve int? tiene sentido.

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

Usaría el segundo, porque probablemente necesito saber de inmediato si la llamada tuvo éxito, y en ese caso preferiría escribir

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

que

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

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

Sí, puede usar el operador de coalescencia para mantener su código como una sola línea si y solo si tiene un valor alternativo que puede usar en el resto de su código. A menudo encuentro que ese no es el caso para mí, y preferiría ejecutar una ruta de código diferente a la que lo haría si pudiera recuperar un valor con éxito.

        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 es realmente anulable, entonces usar 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 distinguir la diferencia entre " No tuve éxito en recuperando 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 que la mayoría de las personas tiende a no usar los parámetros de salida es porque encuentran que la sintaxis es incómoda. Sin embargo, realmente siento que el uso de parámetros de salida es la solución más apropiada para este problema, y ??descubrí que una vez que me acostumbré a ella, la sintaxis me pareció mucho más preferible que devolver un valor nulo.

Si solo hay una forma en la que puede fallar, o si nunca necesita saber por qué falló, diría que probablemente sea más simple y más fácil ir con el valor de retorno que acepta valores nulos.

A la inversa, si hay varias formas en las que podría fallar, y el código de llamada podría saber exactamente por qué falló, entonces vaya con el parámetro de salida y devuelva un código de error en lugar de un bool (alternativamente, podría lanzar una excepción) , pero según su pregunta, parece que ya ha decidido no lanzar una excepción).

En lugar de eso, deberías usar un retén de prueba. Esto parece que la persona que llama no sabe lo que podría pasar?

¿Deberíamos verificar tanto el valor de bool como el de salida, o debo verificar ambas devoluciones nulas y la devolución real?

Permita que el método haga lo que debería, y en el caso de que fallara, informe a la persona que llama que falló, y que la persona que llama haya sido solicitada.

Curiosamente, mi opinión personal se balancea significativamente según la naturaleza del método. Específicamente, si el propósito del método es recuperar un solo valor , en oposición a " hacer algo " ;.

Ej:

bool GetSerialNumber(out string serialNumber)

vs

string GetSerialNumber() // returns null on failure

El segundo se siente más " natural " de alguna manera, y de la misma manera:

bool GetDeviceId(out int id)

vs

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

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

Ah, y yo también tendería a favorecer el lanzamiento de excepciones:

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

Todavía no estoy convencido de por qué estarían tan equivocados. ¿Podemos tener un hilo en eso, Oren? ;-)

No me gusta el " Prueba " de Microsoft patrón en el que " fuera " parámetro 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. Prefiero ver un método codificado como: T GetValue (out bool Successful) o tal vez T GetValue (resultado de GetValueErrorEnum); o T GetValue (resultado de GetValueErrorInfo ); si se necesita algo más allá de un verdadero / falso. Dado que cada tipo de datos tiene un valor legal predeterminado, no hay problema en decidir qué devolver si la función falla. El 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 # ofrecieran verdaderos parámetros de 'copia' covariantes (el llamante asignaría un espacio para el resultado, pasaría un puntero a ese espacio a la función llamada y luego copiaría el espacio asignado al pasado). -en la variable solo después de que la función devuelva).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top