Sobrecarga de método vs parámetro opcional en C # 4.0

¿cuál es mejor? de un vistazo, el parámetro opcional parece mejor (menos código, menos documentación XML, etc.), pero ¿por qué la mayoría de las clases de biblioteca de MSDN usan sobrecarga en lugar de parámetros opcionales?

¿Hay alguna cosa especial que tenga que tomar nota cuando elija usar el parámetro opcional (o sobrecarga)?

Un buen caso de uso para ‘Parámetros opcionales’ junto con ‘Parámetros nombrados’ en C # 4.0 es que nos presenta una alternativa elegante a la sobrecarga de métodos donde se sobrecarga el método en función del número de parámetros.

Por ejemplo, supongamos que quieres que un método foo sea ​​llamado / usado como tal, foo() , foo(1) , foo(1,2) , foo(1,2, "hello") . Con la sobrecarga de métodos implementaría la solución de esta manera,

 ///Base foo method public void DoFoo(int a, long b, string c) { //Do something } /// Foo with 2 params only public void DoFoo(int a, long b) { /// .... DoFoo(a, b, "Hello"); } public void DoFoo(int a) { ///.... DoFoo(a, 23, "Hello"); } ..... 

Con los parámetros opcionales en C # 4.0 implementaría el caso de uso como el siguiente,

 public void DoFoo(int a = 10, long b = 23, string c = "Hello") 

Entonces podrías usar el método como tal: ten en cuenta el uso del parámetro nombrado

 DoFoo(c:"Hello There, John Doe") 

Esta llamada toma el parámetro a valor como 10 y el parámetro b como 23. Otra variante de esta llamada: note que no necesita establecer los valores de los parámetros en el orden tal como aparecen en la firma del método, el parámetro nombrado hace que el valor sea explícito.

 DoFoo(c:"hello again", a:100) 

Otro beneficio de utilizar un parámetro con nombre es que mejora enormemente la legibilidad y, por lo tanto, el mantenimiento del código de los métodos de parámetros opcionales.

Tenga en cuenta cómo un método prácticamente hace que sea redundante tener que definir 3 o más métodos en la sobrecarga de métodos. Esto que he encontrado es un buen caso de uso para usar un parámetro opcional junto con parámetros nombrados.

Los parámetros opcionales proporcionan problemas cuando los expones públicamente como API. Un cambio de nombre de un parámetro puede generar problemas. Cambiar el valor predeterminado genera problemas (consulte, por ejemplo, aquí para obtener información: parámetros opcionales de Advertencias de C # 4.0 )

Además, los parámetros opcionales solo se pueden usar para constantes de tiempo de comstackción. Compare esto:

 public static void Foo(IEnumerable items = new List()) {} // Default parameter value for 'items' must be a compile-time constant 

a esto

 public static void Foo() { Foo(new List());} public static void Foo(IEnumerable items) {} //all good 

Actualizar

Aquí hay material de lectura adicional cuando un constructor con parámetros predeterminados no funciona bien con Reflection .

Creo que sirven para diferentes propósitos. Los parámetros opcionales son para cuando puede usar un valor predeterminado para un parámetro, y el código subyacente será el mismo:

 public CreditScore CheckCredit( bool useHistoricalData = false, bool useStrongHeuristics = true) { // ... } 

Las sobrecargas de métodos son para cuando tiene parámetros mutuamente excluyentes (subconjuntos de). Eso normalmente significa que necesita preprocesar algunos parámetros, o que tiene un código diferente para las diferentes “versiones” de su método (tenga en cuenta que incluso en este caso, algunos parámetros pueden ser compartidos, por eso mencioné “subconjuntos” más arriba) :

 public void SendSurvey(IList customers, int surveyKey) { // will loop and call the other one } public void SendSurvey(Customer customer, int surveyKey) { ... } 

(Escribí sobre esto hace algún tiempo aquí )

Este casi no hace falta decirlo, pero:

No todos los idiomas admiten parámetros opcionales. Si desea que sus bibliotecas sean amigables con esos idiomas, debe usar sobrecargas.

De acuerdo, esto ni siquiera es un problema para la mayoría de las tiendas. Pero puede apostar que es por eso que Microsoft no usa parámetros opcionales en la Biblioteca de clases base.

Los parámetros opcionales tienen que ser los últimos. Por lo tanto, no puede agregar un parámetro adicional a ese método a menos que también sea opcional. Ex:

 void MyMethod(int value, int otherValue = 0); 

Si desea agregar un nuevo parámetro a este método sin sobrecargarlo, tiene que ser opcional. Me gusta esto

 void MyMethod(int value, int otherValue = 0, int newParam = 0); 

Si no puede ser opcional, entonces debe usar la sobrecarga y eliminar el valor opcional para ‘otherValue’. Me gusta esto:

 void MyMethod(int value, int otherValue = 0); void MyMethod(int value, int otherValue, int newParam); 

Supongo que quiere mantener el orden de los parámetros de la misma manera.

Por lo tanto, el uso de parámetros opcionales reduce la cantidad de métodos que necesita tener en su clase, pero es limitado, ya que deben ser los últimos.

Actualización Al llamar a métodos con parámetros opcionales, puede usar parámetros con nombre como este:

 void MyMethod(int value, int otherValue = 0, int newValue = 0); MyMethod(10, newValue: 10); // Here I omitted the otherValue parameter that defaults to 0 

Entonces, los parámetros opcionales le dan a quien llama más posibilidades.

Una última cosa. Si usa la sobrecarga de métodos con una implementación, haga lo siguiente:

 void MyMethod(int value, int otherValue) { // Do the work } void MyMethod(int value) { MyMethod(value, 0); // Do the defaulting by method overloading } 

Luego, cuando llame a ‘MyMethod’ de esta manera:

 MyMethod(100); 

Se generarán 2 llamadas a métodos. Pero si usa parámetros opcionales, solo hay una implementación de ‘MyMethod’ y, por lo tanto, solo una llamada a un método.

Ninguno es definitivamente “mejor” que el otro. Ambos tienen su lugar para escribir un buen código. Se deben usar parámetros opcionales si los parámetros pueden tener un valor predeterminado. La sobrecarga de métodos se debe usar cuando la diferencia en la firma va más allá de no definir los parámetros que podrían tener valores predeterminados (como que el comportamiento difiere dependiendo de qué parámetros se pasan y cuáles se dejan por defecto).

 // this is a good candidate for optional parameters public void DoSomething(int requiredThing, int nextThing = 12, int lastThing = 0) // this is not, because it should be one or the other, but not both public void DoSomething(Stream streamData = null, string stringData = null) // these are good candidates for overloading public void DoSomething(Stream data) public void DoSomething(string data) // these are no longer good candidates for overloading public void DoSomething(int firstThing) { DoSomething(firstThing, 12); } public void DoSomething(int firstThing, int nextThing) { DoSomething(firstThing, nextThing, 0); } public void DoSomething(int firstThing, int nextThing, int lastThing) { ... } 

Un buen lugar para usar el parámetro opcional es WCF, ya que no admite la sobrecarga de métodos.

¿Qué pasa con una tercera opción: pasar una instancia de una clase con propiedades correspondientes a varios “parámetros opcionales”.

Esto proporciona el mismo beneficio que los parámetros nominales y opcionales, pero creo que esto a menudo es mucho más claro. Le da la oportunidad de agrupar lógicamente los parámetros si es necesario (es decir, con la composición) y encapsular también alguna validación básica.

Además, si espera que los clientes que consumn sus métodos realicen cualquier tipo de metaprogtwigción (como crear expresiones linq que involucren sus métodos), creo que mantener la firma del método simple tiene sus ventajas.

Esto no es realmente una respuesta a la pregunta original, sino un comentario sobre la respuesta de @ NileshGule, pero:

a) No tengo suficientes puntos de reputación para comentar

b) Varias líneas de código son bastante difíciles de leer en los comentarios

Nilesh Gule escribió:

Una de las ventajas de utilizar parámetros opcionales es que no necesita tener que realizar una comprobación condicional en sus métodos, como si una cadena fuera nula o estuviera vacía si uno de los parámetros de entrada fuera una cadena. Como se asignaría un valor predeterminado al parámetro opcional, la encoding defensiva se reducirá en gran medida.

Esto es realmente incorrecto, aún debe verificar nulos:

 void DoSomething(string value = "") // Unfortunately string.Empty is not a compile-time constant and cannot be used as default value { if(value == null) throw new ArgumentNullException(); } DoSomething(); // OK, will use default value of "" DoSomething(null); // Will throw 

Si proporciona una referencia de cadena nula, no se reemplazará por el valor predeterminado. Por lo tanto, aún debe verificar los parámetros de entrada para los valores nulos.

Una de las ventajas de utilizar parámetros opcionales es que no necesita tener que realizar una comprobación condicional en sus métodos, como si una cadena fuera nula o estuviera vacía si uno de los parámetros de entrada fuera una cadena. Como se asignaría un valor predeterminado al parámetro opcional, la encoding defensiva se reducirá en gran medida.

Los parámetros con nombre dan la flexibilidad de pasar valores de parámetros en cualquier orden.

Para abordar su primera pregunta,

¿Por qué la mayoría de las clases de biblioteca de MSDN usan sobrecarga en lugar de parámetros opcionales?

Es por compatibilidad con versiones anteriores.

Cuando abre un proyecto C # 2, 3.0 o 3.5 en VS2010, se actualiza automáticamente.

Solo imagine los inconvenientes que crearía si cada una de las sobrecargas utilizadas en el proyecto tuviera que convertirse para coincidir con la correspondiente statement de parámetro opcional.

Además, como dice el refrán, “¿por qué arreglar lo que no está roto?”. No es necesario reemplazar las sobrecargas que ya funcionan con nuevas implementaciones.