Estaba viendo la charla de Anders sobre C # 4.0 y la vista previa de C # 5.0 , y me hizo pensar cuando los parámetros opcionales están disponibles en C #, ¿cuál será la forma recomendada de declarar métodos que no necesitan todos los parámetros especificados?
Por ejemplo, algo así como la FileStream
clase tiene alrededor de quince constructores diferentes que se pueden dividir en 'familias' lógicas, por ejemplo, los de abajo de una cadena, los de an IntPtr
y los de a SafeFileHandle
.
FileStream(string,FileMode);
FileStream(string,FileMode,FileAccess);
FileStream(string,FileMode,FileAccess,FileShare);
FileStream(string,FileMode,FileAccess,FileShare,int);
FileStream(string,FileMode,FileAccess,FileShare,int,bool);
Me parece que este tipo de patrón podría simplificarse teniendo tres constructores en su lugar, y usando parámetros opcionales para los que pueden estar predeterminados, lo que haría que las diferentes familias de constructores sean más distintas [nota: sé que este cambio no será hecho en la BCL, estoy hablando hipotéticamente para este tipo de situación].
¿Qué piensas? A partir de C # 4.0, ¿tendrá más sentido convertir los grupos de constructores y métodos estrechamente relacionados en un método único con parámetros opcionales, o hay una buena razón para seguir con el mecanismo tradicional de sobrecarga múltiple?
fuente
Cuando una sobrecarga de método normalmente realiza lo mismo con un número diferente de argumentos, se utilizarán los valores predeterminados.
Cuando una sobrecarga de método realiza una función de manera diferente en función de sus parámetros, se seguirá utilizando la sobrecarga.
Usé opcional en mis días de VB6 y desde entonces lo he perdido, reducirá una gran cantidad de duplicación de comentarios XML en C #.
fuente
He estado usando Delphi con parámetros opcionales desde siempre. En su lugar, cambié a usar sobrecargas.
Porque cuando va a crear más sobrecargas, invariablemente entrará en conflicto con un formulario de parámetro opcional, y luego tendrá que convertirlos a no opcionales de todos modos.
Y me gusta la idea de que generalmente hay un súper método, y el resto son envoltorios más simples sobre ese.
fuente
Foo(A, B, C)
requiereFoo(A)
,Foo(B)
,Foo(C)
,Foo(A, B)
,Foo(A, C)
,Foo(B, C)
.Definitivamente usaré la función de parámetros opcionales de 4.0. Se deshace de lo ridículo ...
... y coloca los valores justo donde la persona que llama pueda verlos ...
Mucho más simple y mucho menos propenso a errores. De hecho, he visto esto como un error en el caso de sobrecarga ...
Todavía no he jugado con el complier 4.0, pero no me sorprendería saber que el complier simplemente emite las sobrecargas por ti.
fuente
Los parámetros opcionales son esencialmente una pieza de metadatos que dirige a un compilador que está procesando una llamada a un método para que inserte los valores predeterminados apropiados en el sitio de la llamada. Por el contrario, las sobrecargas proporcionan un medio por el cual un compilador puede seleccionar uno de varios métodos, algunos de los cuales pueden proporcionar valores predeterminados por sí mismos. Tenga en cuenta que si uno intenta llamar a un método que especifica parámetros opcionales a partir de código escrito en un lenguaje que no los admite, el compilador requerirá que se especifiquen los parámetros "opcionales", pero dado que llamar a un método sin especificar un parámetro opcional es equivalente a llamarlo con un parámetro igual al valor predeterminado, no hay ningún obstáculo para que tales lenguajes llamen a tales métodos.
Una consecuencia importante de la vinculación de parámetros opcionales en el sitio de llamada es que se les asignarán valores basados en la versión del código de destino que está disponible para el compilador. Si un ensamblado
Foo
tiene un métodoBoo(int)
con un valor predeterminado de 5, y el ensambladoBar
contiene una llamada aFoo.Boo()
, el compilador lo procesará comoFoo.Boo(5)
. Si el valor predeterminado se cambia a 6 yFoo
se vuelve a compilar el ensamblado ,Bar
continuará llamando aFoo.Boo(5)
menos o hasta que se vuelva a compilar con esa nueva versión deFoo
. Por lo tanto, se debe evitar el uso de parámetros opcionales para cosas que puedan cambiar.fuente
void Foo(int value) … void Foo() { Foo(42); }
. Desde el exterior, la persona que llama no sabe qué valor predeterminado se utiliza, ni cuándo podría cambiar; uno tendría que monitorear la documentación escrita para eso. Los valores predeterminados para los parámetros opcionales pueden verse simplemente como eso: documentación en código cuál es el valor predeterminado.Se puede argumentar si deben usarse o no argumentos opcionales o sobrecargas, pero lo más importante es que cada uno tiene su propia área donde son insustituibles.
Los argumentos opcionales, cuando se usan en combinación con argumentos con nombre, son extremadamente útiles cuando se combinan con algunas listas de argumentos largos con todos los opcionales de llamadas COM.
Las sobrecargas son extremadamente útiles cuando el método puede operar en muchos tipos de argumentos diferentes (solo uno de los ejemplos) y realiza conversiones internamente, por ejemplo; simplemente lo alimenta con cualquier tipo de datos que tenga sentido (que sea aceptado por alguna sobrecarga existente). No se puede superar eso con argumentos opcionales.
fuente
Espero con interés los parámetros opcionales porque mantiene los valores predeterminados más cercanos al método. Entonces, en lugar de docenas de líneas para las sobrecargas que simplemente llaman al método "expandido", simplemente defina el método una vez y podrá ver lo que los parámetros opcionales tienen por defecto en la firma del método. Prefiero mirar:
En lugar de esto:
Obviamente, este ejemplo es realmente simple, pero en el caso del OP con 5 sobrecargas, las cosas pueden llenarse muy rápido.
fuente
Uno de mis aspectos favoritos de los parámetros opcionales es que ves lo que sucede con tus parámetros si no los proporcionas, incluso sin ir a la definición del método. Visual Studio simplemente le mostrará el valor predeterminado para el parámetro cuando escriba el nombre del método. Con un método de sobrecarga, no puede leer la documentación (si está disponible) o navegar directamente a la definición del método (si está disponible) y al método que envuelve la sobrecarga.
En particular: el esfuerzo de documentación puede aumentar rápidamente con la cantidad de sobrecargas, y probablemente terminará copiando comentarios ya existentes de las sobrecargas existentes. Esto es bastante molesto, ya que no produce ningún valor y rompe el principio DRY ). Por otro lado, con un parámetro opcional, hay exactamente un lugar donde todos los parámetros están documentados y puede ver su significado, así como sus valores predeterminados mientras escribe.
Por último, pero no menos importante, si usted es el consumidor de una API, es posible que ni siquiera tenga la opción de inspeccionar los detalles de implementación (si no tiene el código fuente) y, por lo tanto, no tendrá la oportunidad de ver a qué súper método están sobrecargados. están envolviendo. Por lo tanto, está atascado leyendo el documento y esperando que todos los valores predeterminados aparezcan allí, pero este no es siempre el caso.
Por supuesto, esta no es una respuesta que maneje todos los aspectos, pero creo que agrega una que no se ha cubierto hasta ahora.
fuente
Si bien son (¿supuestamente?) Dos formas conceptualmente equivalentes disponibles para modelar su API desde cero, desafortunadamente tienen una diferencia sutil cuando necesita considerar la compatibilidad con versiones anteriores del tiempo de ejecución para sus antiguos clientes en la naturaleza. Mi colega (¡gracias Brent!) Me señaló esto maravillosa publicación: Problemas de versiones con argumentos opcionales . Algunos lo citan:
fuente
Una advertencia de los parámetros opcionales es el control de versiones, donde una refactorización tiene consecuencias no deseadas. Un ejemplo:
Código inicial
Suponga que esta es una de las muchas personas que llaman del método anterior:
Aquí el evento no es silencioso y se trata como crítico.
Ahora digamos que después de una refactorización encontramos que todos los errores solicitan al usuario de todos modos, por lo que ya no necesitamos la bandera silenciosa. Entonces lo quitamos.
Después de refactorizar
La llamada anterior aún se compila, y digamos que se desliza por el refactor sin cambios:
Ahora
false
tendrá un efecto no deseado, el evento ya no se tratará como crítico.Esto podría resultar en un defecto sutil, ya que no habrá error de compilación o tiempo de ejecución (a diferencia de otras advertencias de opcionales, como este o este ).
Tenga en cuenta que existen muchas formas de este mismo problema. Aquí se describe otra forma .
Nótese también que estrictamente el uso de parámetros con nombre al llamar al método evitará la emisión, como la siguiente:
HandleError("Disk is full", silent:false)
. Sin embargo, puede que no sea práctico asumir que todos los demás desarrolladores (o usuarios de una API pública) lo harán.Por estas razones, evitaría el uso de parámetros opcionales en una API pública (o incluso un método público si pudiera usarse ampliamente) a menos que haya otras consideraciones convincentes.
fuente
Tanto el parámetro opcional como la sobrecarga del método tienen su propia ventaja o desventaja. Depende de su preferencia para elegir entre ellos.
Parámetro opcional: disponible solo en .Net 4.0. El parámetro opcional reduce el tamaño de su código. No se puede definir el parámetro out y ref
métodos sobrecargados: puede definir parámetros de salida y de referencia. El tamaño del código aumentará, pero los métodos sobrecargados son fáciles de entender.
fuente
En muchos casos, se utilizan parámetros opcionales para cambiar la ejecución. Por ejemplo:
El parámetro de descuento aquí se usa para alimentar la declaración if-then-else. Existe el polimorfismo que no se reconoció y luego se implementó como una declaración if-then-else. En tales casos, es mucho mejor dividir los dos flujos de control en dos métodos independientes:
De esta manera, incluso hemos protegido a la clase para que no reciba una llamada sin descuento. Esa llamada significaría que la persona que llama piensa que existe el descuento, pero de hecho no hay ningún descuento. Tal malentendido puede causar fácilmente un error.
En casos como este, prefiero no tener parámetros opcionales, sino obligar al llamador a seleccionar explícitamente el escenario de ejecución que se adapte a su situación actual.
La situación es muy similar a tener parámetros que pueden ser nulos. Esa es igualmente mala idea cuando la implementación se reduce a declaraciones como
if (x == null)
.Puede encontrar un análisis detallado en estos enlaces: Evitar parámetros opcionales y Evitar parámetros nulos
fuente
Para agregar una obviedad cuando usar una sobrecarga en lugar de opcionales:
Siempre que tenga una serie de parámetros que solo tienen sentido juntos, no introduzca opcionales en ellos.
O de manera más general, siempre que las firmas de su método habiliten patrones de uso que no tienen sentido, restrinja el número de permutaciones de posibles llamadas. Por ejemplo, mediante el uso de sobrecargas en lugar de opcionales (esta regla también se cumple cuando tiene varios parámetros del mismo tipo de datos, por cierto; aquí, dispositivos como métodos de fábrica o tipos de datos personalizados pueden ayudar).
Ejemplo:
fuente