Pregunta

Tengo varios métodos similares, por ejemplo, por ejemplo. CalculatePoint (...) y CalculateListOfPoints (...). En ocasiones, es posible que no tengan éxito y necesiten indicárselo a la persona que llama. Para CalculateListOfPoints, que devuelve una Lista genérica, podría devolver una lista vacía y requerir que la persona que llama verifique esto; sin embargo, Point es un tipo de valor, por lo que no puedo devolver el valor nulo allí.

Idealmente, me gustaría que los métodos se 'vean' similares; Una solución podría ser definirlos como

public Point CalculatePoint(... out Boolean boSuccess);
public List<Point> CalculateListOfPoints(... out Boolean boSuccess);

o alternativamente para devolver un punto? para CalculatePoint, y devuelve nulo para indicar falla. Eso significaría tener que volver al tipo no anulable, lo que parece excesivo.

Otra ruta sería devolver el boSuccess booleano, tener el resultado (Punto o Lista) como un parámetro de 'salida' y llamarlos TryToCalculatePoint o algo así ...

¿Cuál es la mejor práctica?

Editar: ¡No quiero usar Excepciones para el control de flujo! A veces se espera un fallo.

¿Fue útil?

Solución

Personalmente, creo que usaría la misma idea que TryParse (): usar un parámetro de salida para generar el valor real y devolver un valor booleano que indique si la llamada fue exitosa o no

public bool CalculatePoint (... out Resultado del punto);

No soy fanático de usar la excepción para " normal " comportamientos (si espera que la función no funcione para algunas entradas).

Otros consejos

¿Por qué fallarían? Si es por algo que el llamante ha hecho (es decir, los argumentos proporcionados), lanzar ArgumentException es completamente apropiado. Un método Try [...] que evita la excepción está bien.

Creo que es una buena idea proporcionar la versión que lanza una excepción, de modo que las personas que llaman que esperan que siempre proporcionen buenos datos recibirán un mensaje adecuadamente fuerte (es decir, una excepción) si alguna vez están equivocados.

Otra alternativa es lanzar una excepción. Sin embargo, generalmente solo desea lanzar excepciones en " casos excepcionales " ;.

Si los casos de falla son comunes (y no excepcionales), entonces ya ha enumerado sus dos opciones. EDIT : puede haber una convención en su proyecto sobre cómo manejar estos casos no excepcionales (si uno debe devolver el éxito o el objeto). Si no existe una convención, entonces estoy de acuerdo con lucasbfr y le sugiero que devuelva el éxito (lo que concuerda con el diseño de TryParse (...)).

Si la falla es por una razón específica, entonces creo que está bien devolver nulo, o bool y tener un parámetro de salida. Sin embargo, si devuelve el valor nulo independientemente del error, no lo recomiendo. Las excepciones brindan un amplio conjunto de información que incluye la razón por la que SE FALLÓ algo, si todo lo que devuelves es nulo, entonces, ¿cómo saber si es porque los datos son incorrectos, te has quedado sin memoria o algún otro comportamiento extraño?

Incluso en .net, TryParse tiene un hermano Parse para que puedas obtener la excepción si quieres.

Si proporcionara un método TrySomething, también proporcionaría un método Something que produjo una excepción en caso de falla. Entonces le toca a la persona que llama.

El modelo que he usado es el mismo que MS usa con los métodos TryParse de varias clases.

Tu código original:
punto público PuntoPuntosPuntos (... de Boolean boSuccess);
public List CalculateListOfPoints (... fuera Boolean boSuccess);

Se convertiría en public bool CalculatePoint (... out (or ref) Point CalculatedValue);
public bool CalculateListOfPoints (... out (o ref) List CalculatedValues);

Básicamente, usted hace que el éxito / fracaso sea el valor de retorno.

Para resumir, hay un par de enfoques que puede tomar:

  1. Cuando el tipo de retorno es un tipo de valor, como un Punto, use la función de Nullable de C # y devuelva un Punto? (también conocido como Nullable), de esa manera aún puede devolver nulo en caso de error
  2. Lanzar una excepción cuando hay un error. Todo el argumento / discusión sobre qué es y qué no es " excepcional " es un punto discutible, es su API, usted decide cuál es el comportamiento excepcional.
  3. Adopte un modelo similar al implementado por Microsoft en los tipos base como Int32, proporcione CalculatePoint y TryCalculatePoint (int32.Parse e int32.TryParse) y tenga un lanzamiento y uno devuelva un bool.
  4. Devuelva una estructura genérica de sus métodos que tenga dos propiedades, bool Success y GenericType Value.

Dependiendo del escenario, tiendo a usar una combinación de devolver nulo o lanzar una excepción, ya que parecen " más limpias " para mí y encajar mejor con el código base existente en la empresa para la que trabajo. Así que mi mejor práctica personal sería los enfoques 1 y 2.

Depende principalmente del comportamiento de sus métodos y de su uso.

Si la falla es común y no crítica, entonces haga que sus métodos devuelvan un valor booleano que indique su éxito y utilicen un parámetro de salida para transmitir el resultado. Al buscar una clave en un hash, al intentar leer datos en un socket no bloqueante cuando no hay datos disponibles, todos estos ejemplos entran en esa categoría.

Si la falla es inesperada, devuelva directamente el resultado y transmita los errores con excepciones. La apertura de un archivo de solo lectura, la conexión a un servidor TCP, son buenos candidatos.

Y a veces ambas formas tienen sentido ...

Devuelve Point.Empty . Es un patrón de diseño .NET para devolver un campo especial cuando desea verificar si la creación de la estructura fue exitosa. Evite los parámetros out cuando pueda.

public static readonly Point Empty

Un patrón con el que estoy experimentando es devolver un Quizás . Tiene la semántica del patrón TryParse , pero una firma similar al patrón de retorno nulo en error.

Todavía no estoy convencido de una forma u otra, pero lo ofrezco para su consideración colectiva. Tiene el beneficio de no requerir que se defina una variable antes de la llamada al método para retener el parámetro out en el sitio de la llamada del método. También podría extenderse con una colección de Errores o Mensajes para indicar el motivo del error.

La clase Quizás se parece a esto:

/// <summary>
/// Represents the return value from an operation that might fail
/// </summary>
/// <typeparam name="T"></typeparam>
public struct Maybe<T>
{
    T _value;
    bool _hasValue;


    public Maybe(T value)
    {
        _value = value;
        _hasValue = true;
    }

    public Maybe()
    {
        _hasValue = false;
        _value = default(T);
    }


    public bool Success
    {
        get { return _hasValue; }
    }


    public T Value
    {
        get 
            { // could throw an exception if _hasValue is false
              return _value; 
            }
    }
}

Yo diría que la mejor práctica es un valor de retorno significa éxito, y un excepción significa falla.

No veo ninguna razón en los ejemplos que proporcionó que no debería usar excepciones en caso de falla.

El uso de una excepción es una mala idea en algunos casos (especialmente al escribir un servidor). Necesitarías dos sabores del método. También mire la clase de diccionario para obtener una indicación de lo que debe hacer.

// NB:  A bool is the return value. 
//      This makes it possible to put this beast in if statements.
public bool TryCalculatePoint(... out Point result) { }

public Point CalculatePoint(...)
{
   Point result;
   if(!TryCalculatePoint(... out result))
       throw new BogusPointException();
   return result;
}

¡Lo mejor de ambos mundos!

El bool TrySomething () es al menos una práctica, que funciona bien para los métodos de análisis de .net, pero no creo que me guste en general.

Lanzar una excepción a menudo es algo bueno, aunque no debe usarse para situaciones que esperaría suceder en muchas situaciones normales, y tiene un costo de rendimiento asociado.

Devolver nulo cuando es posible en la mayoría de los casos está bien, cuando no quieres una excepción.

However : su enfoque es un poco de procedimiento. ¿Qué hay de crear algo como una clase PointCalculator, tomando los datos necesarios como parámetros en el constructor? Luego llama a CalculatePoint en él y accede al resultado a través de propiedades (propiedades separadas para Point y para Success).

No desea lanzar excepciones cuando se espera que ocurra algo, ya que @Kevin indica que las excepciones son para casos excepcionales.

Debes devolver algo que se espera por el 'error', generalmente nulo es mi elección de mala devolución.

La documentación de su método debe informar a los usuarios de qué esperar cuando los datos no se computan .

Una vez escribimos un Framework completo en el que todos los métodos públicos devolvían verdadero (ejecutado con éxito) o falso (se produjo un error). Si necesitábamos devolver un valor, usamos parámetros de salida. Contrariamente a la creencia popular, esta forma de programación realmente simplificó gran parte de nuestro código.

Bueno, con Point, puede devolver Point.Empty como valor de retorno en caso de fallo. Ahora todo lo que realmente hace es devolver un punto con 0 para el valor de X e Y, así que si ese puede ser un valor de retorno válido, me mantendría alejado de eso, pero si su método nunca devolverá un punto (0,0) , entonces puedes usar eso.

Lo siento, acabo de recordar el tipo de Nullable, deberías mirar eso. Sin embargo, no estoy muy seguro de cuál es la sobrecarga.

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