c# code-smell out-parameters

c# - ¿Qué es una mala práctica al usar los parámetros?



code-smell out-parameters (10)

¿Hay algún principio a tener en cuenta al usar los parámetros? ¿O puedo verlos como una buena forma de permitir que un método devuelva múltiples valores?

¿Qué tenían en mente los diseñadores de idiomas cuando especificaban el parámetro out?

Editar después de un pensamiento:

Como estoy pensando en ello ahora, me inclinaría a decir que el uso excesivo de parámetros externos podría ser indicio de un olor codificado. Si un método necesita devolver instancias de más de 1 tipo específico, implica que el método tiene más de una preocupación, lo cual es una violación de SRP.


A menudo uso tipos anulables al devolver tipos de valor más un indicador de éxito en lugar de devolver un bool y usar un parámetro out para el tipo de valor. Incluso envuelvo el método TryParse para que devuelva un valor nulo.

Los parámetros de salida son malos también porque deben usar variables o campos locales. No puede refactorizar fácilmente su código para usar propiedades.

Casi siempre es mejor crear una clase / estructura personalizada, usar tuplas o tipos anulables.


El uso de parámetros en general es una mala práctica. Típicamente indica un método que está haciendo más de una cosa bien definida. La única vez que recomiendo usarlos es para los escenarios de Interop, donde a veces son necesarios debido a la firma de la API.


Generalmente veo los parámetros como un "olor a código" e intentaré refactorizar a un enfoque diferente si me encuentro con ellos. Como alguien ya ha señalado, comúnmente la refactorización será para crear una nueva clase que agregue los valores que iba a devolver a través de params.


Hay dos situaciones en las que no veo ningún problema en el uso de out parámetros, y estas situaciones ocurren con mayor frecuencia una junto a la otra:

  1. Cuando un método tiene que devolver más de un valor;
  2. Cuando se usa en métodos privados , específicamente en sub rutinas imperativas con un nivel de abstracción más bajo.

Comentarios sobre el razonamiento:

  • A veces puede tener operaciones que cambian más de un estado, por lo que puede operar en un grupo de objetos que pertenecen a un solo proceso con solo una llamada de función (que puede tener más significado semántico);
  • Dado que se supone que las variables dentro de la clase tienen una alta cohesión, por lo tanto un alto acoplamiento, creo que "no hace daño" tener métodos que usen parámetros. Eso es exactamente lo opuesto para las funciones públicas que espera encontrar en las API;
  • En C #, los parámetros de salida parecen mostrarse mucho en el código derivado de más API de procedimientos portados a OO. Las rutinas numéricas y científicas son el mejor ejemplo que puedo encontrar (alglib, AForge, etc.). Esto refuerza mi idea de que está bien recurrir a detalles de implementación interna, que pueden ser muy procesales dependiendo del dominio del problema.

Como ejemplo de una operación semánticamente "atómica" que cambia dos variables relacionadas pero independientes, aquí va un código de Python y cómo lo porté a C # (en un método privado):

Pitón:

p1, p2 = FindClosestPair(p, listOfValues)

DO#:

Point3D p1, p2; FindClosestPair(p, listOfValues, out p1, out p2);

No podría pensar en una forma más directa de hacerlo (pero me encantaría saber posibles alternativas).


Las herramientas de diseño de clase dicen específicamente "Evitar los parámetros" , con una larga discusión sobre el razonamiento.

A menos que haya una buena razón para usar un parámetro de salida para un método, generalmente es mejor crear una clase o estructura personalizada para contener los resultados. El uso de parámetros reduce la usabilidad de una API.


Mientras out parámetros tienen su uso, antes de usar uno debes detenerte y pensar si realmente lo necesitas. Es posible que pueda llegar a una implementación más simple y limpia que no requiera out .

why-are-out-parameters-in-net-a-bad-idea tiene más información.


Solo lo mismo que los valores de retorno, es decir. tenga cuidado con todo lo que la clase llamada retendrá y usará más tarde. En general, prefiero un objeto envoltorio; hace que el código de llamada sea un poco más legible.


Una advertencia importante a tener en cuenta es que antes de .NET 3.5 SP1, los métodos que toman o devuelven tipos de valores nunca se incluyen. Esto podría tener grandes implicaciones de rendimiento para los métodos pequeños y frecuentemente llamados, como los de una biblioteca matemática de vectores.

Esta es una de las razones principales por las que la biblioteca de vectores de vectores SlimDX proporciona sobrecargas que usan parámetros en lugar de valores de retorno; sirve como una potencial micro-optimización.


Utilizo parámetros cuando escribo métodos estáticos TryParse en mis clases. Solo para mantenerme sincronizado con el CLR.

Aparte de eso, probablemente sea una mejor idea devolver una clase que tenga lo que necesita.


Personalmente, no estoy muy interesado en out parámetros más allá de la convención TryParse establecida. Prefiero devolver una instancia de un tipo específico que encapsula los diferentes parámetros, ya que hace que el código sea mucho más fácil de leer imo.

Si no desea definir tipos específicos para esto, puede usar el tipo Tuple en el próximo .NET framework. Las tuplas son básicamente un grupo de valores envueltos en un tipo. Se usan mucho en, por ejemplo, F #.