¿Debería declarar métodos usando sobrecargas o parámetros opcionales en C # 4.0?


Estaba viendo la charla de Anders sobre C# 4.0 y la vista previa de C# 5.0, y me hizo pensar en cuándo están disponibles los parámetros opcionales en C# ¿cuál será la forma recomendada de declarar métodos que no necesitan todos los parámetros especificados?

Por ejemplo, algo como la clase FileStream tiene unos 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 ser predeterminados, lo que haría que las diferentes familias de constructores sean más distintas [nota: Sé que este cambio no se hará en el BCL, estoy hablando hipotéticamente para este tipo de situación].

¿Qué piensas? De C # 4.0 tendrá más sentido hacer grupos estrechamente relacionados de constructores y métodos a método único con parámetros opcionales, o hay una buena razón para seguir con el mecanismo tradicional de sobrecarga múltiple?

Author: Greg Beech, 2008-10-31

13 answers

Consideraría lo siguiente:

  • ¿Necesita que su código se use desde idiomas que no admiten parámetros opcionales? Si es así, considere incluir las sobrecargas.
  • ¿Tienes algún miembro en tu equipo que se oponga violentamente a los parámetros opcionales? (A veces es más fácil vivir con una decisión que no te gusta que argumentar el caso.)
  • ¿Está seguro de que sus valores predeterminados no cambiarán entre compilaciones de su código, o si podrían, sus llamantes estarán de acuerdo con ¿eso?

No he comprobado cómo van a funcionar los valores predeterminados, pero asumiría que los valores predeterminados se incorporarán al código de llamada, lo mismo que las referencias a los campos const. Eso suele estar bien-los cambios a un valor predeterminado son bastante significativos de todos modos-pero esas son las cosas a considerar.

 107
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-30 21:55:12

Cuando una sobrecarga de método normalmente realiza lo mismo con un número diferente de argumentos, entonces se usará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 VB6 y desde entonces lo he perdido, reducirá una gran cantidad de duplicación de comentarios XML en C#.

 17
Author: cfeduke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-30 21:47:37

Siempre he estado usando Delphi, con parámetros opcionales. he cambiado a usar sobrecargas en su lugar.

Porque cuando vayas a crear más sobrecargas, invariablemente te encontrarás con un formulario de parámetros opcional; y entonces tendrás que convertirlos a no opcionales de todos modos.

Y me gusta la noción de que generalmente hay un método super, y el resto son envoltorios más simples alrededor de ese.

 11
Author: Ian Boyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-24 16:55:56

Definitivamente usaré la función de parámetros opcional de 4.0. Se deshace de lo ridículo ...

public void M1( string foo, string bar )
{
   // do that thang
}

public void M1( string foo )
{
  M1( foo, "bar default" ); // I have always hated this line of code specifically
}

... y pone los valores justo donde la persona que llama puede verlos ...

public void M1( string foo, string bar = "bar default" )
{
   // do that thang
}

Mucho más simple y mucho menos propenso a errores. En realidad he visto esto como un error en el caso de sobrecarga ...

public void M1( string foo )
{
   M2( foo, "bar default" );  // oops!  I meant M1!
}

No he jugado con el 4.0 compilador, pero yo no sería la sorpresa de que el compilador simplemente emite las sobrecargas para usted.

 7
Author: JP Alioto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-24 16:53:38

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 insertar 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 una serie de 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 desde código escrito en un lenguaje que no los soporta, el compilador requerirá que se deben especificar 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 obstáculo para que dichos lenguajes llamen a dichos métodos.

Una consecuencia significativa del enlace 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étodo Boo(int) con un valor predeterminado valor de 5, y assembly Bar contiene una llamada a Foo.Boo(), el compilador lo procesará como Foo.Boo(5). Si el valor predeterminado se cambia a 6 y el ensamblado Foo se recompila, Bar continuará llamando a Foo.Boo(5) a menos que o hasta que se recompile con esa nueva versión de Foo. Por lo tanto, uno debe evitar el uso de parámetros opcionales para cosas que podrían cambiar.

 6
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-21 01:41:51

Espero con interés los parámetros opcionales porque mantiene lo que los valores predeterminados están más cerca del método. Así que en lugar de docenas de líneas para las sobrecargas que solo llaman al método "expandido", solo define el método una vez y puede ver cuáles son los parámetros opcionales predeterminados en la firma del método. Prefiero mirar:

public Rectangle (Point start = Point.Zero, int width, int height)
{
    Start = start;
    Width = width;
    Height = height;
}

En lugar de esto:

public Rectangle (Point start, int width, int height)
{
    Start = start;
    Width = width;
    Height = height;
}

public Rectangle (int width, int height) :
    this (Point.Zero, width, height)
{
}

Obviamente este ejemplo es muy simple, pero el caso en el OP con 5 sobrecargas, las cosas pueden llenarse real breve.

 3
Author: Mark A. Nicolosi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-30 22:00:03

Se puede argumentar si se deben usar argumentos opcionales o sobrecargas o no, pero lo más importante es que cada uno tiene su propia área donde son irremplazables.

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 method es capaz de operar en muchos tipos de argumentos diferentes( solo uno de los ejemplos), y hace castings internamente, por ejemplo; simplemente lo alimentas con cualquier tipo de datos que tenga sentido (que sea aceptado por alguna sobrecarga existente). No se puede superar con argumentos opcionales.

 3
Author: mr.b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-21 10:16:13

Uno de mis aspectos favoritos de los parámetros opcionales es que vea lo que sucede con sus parámetros si no los proporciona, 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 usted está atascado con leer la documentación (si incluso está disponible) o con navegar directamente a la definición del método (si está disponible) y al método que la sobrecarga envolver.

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 SECO). Por otro lado, con un parámetro opcional hay exactamente un lugar donde se documentan todos los parámetros y se ve su significado, así como sus valores predeterminados mientras que teclear.

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 la implementación (si no tiene el código fuente) y, por lo tanto, no tenga la oportunidad de ver a qué súper método están envolviendo los sobrecargados. Por lo tanto, está atascado con la lectura del documento y la esperanza de que todos los valores predeterminados se enumeran allí, pero este no es siempre el caso.

Por supuesto, esta no es una respuesta que maneje todos los aspectos, pero creo que agrega uno que no se ha cubierto hasta ahora.

 3
Author: HimBromBeere,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-10 04:23:26

Una advertencia de los parámetros opcionales es el control de versiones, donde un refactor tiene consecuencias no deseadas. Un ejemplo:

Código Inicial

public string HandleError(string message, bool silent=true, bool isCritical=true)
{
  ...
}

Supongamos que este es uno de los muchos llamantes del método anterior:

HandleError("Disk is full", false);

Aquí el evento no es silencioso y es tratado como crítico.

Ahora digamos que después de un refactor encontramos que todos los errores le piden al usuario de todos modos, por lo que ya no necesitamos la bandera silenciosa. Así que lo quitamos.

Después del refactor

El la llamada anterior todavía compila, y digamos que se desliza a través del refactor sin cambios:

public string HandleError(string message, /*bool silent=true,*/ bool isCritical=true)
{
  ...
}

...

// Some other distant code file:
HandleError("Disk is full", false);

Ahora false tendrá un efecto no deseado, el evento ya no será tratado 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 this o this).

Note que hay muchas formas de este mismo problema. Se describe otra forma aquí.

Tenga en cuenta también que usar estrictamente parámetros con nombre al llamar al método evitará el problema, como así: HandleError("Disk is full", silent:false). Sin embargo, puede no ser práctico asumir que todos los demás desarrolladores (o usuarios de una API pública) lo harán.

Por estas razones, evitaría usar parámetros opcionales en una API pública (o incluso en un método público si se pudiera usar ampliamente) a menos que haya otras consideraciones convincentes.

 1
Author: Zach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-07 23:00:35

Ambos parámetros opcionales, la sobrecarga del método tienen su propia ventaja o disadvantage.it depende de su preferencia para elegir entre ellos.

Parámetro Opcional: disponible solo en. Net 4.0. parámetro opcional reducir el tamaño del código. No se puede definir el parámetro out y ref

Métodos Sobrecargados: Puede Definir y ref parámetros. El tamaño del código aumentará, pero los métodos sobrecargados son fáciles de entender.

 0
Author: sushil pandey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-31 07:27:37

En muchos casos se utilizan parámetros opcionales para cambiar la ejecución. Por ejemplo:

decimal GetPrice(string productName, decimal discountPercentage = 0)
{

    decimal basePrice = CalculateBasePrice(productName);

    if (discountPercentage > 0)
        return basePrice * (1 - discountPercentage / 100);
    else
        return basePrice;
}

El parámetro Discount aquí se usa para alimentar la instrucción if-then-else. Está el polimorfismo que no fue reconocido, 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:

decimal GetPrice(string productName)
{
    decimal basePrice = CalculateBasePrice(productName);
    return basePrice;
}

decimal GetPrice(string productName, decimal discountPercentage)
{

    if (discountPercentage <= 0)
        throw new ArgumentException();

    decimal basePrice = GetPrice(productName);

    decimal discountedPrice = basePrice * (1 - discountPercentage / 100);

    return discountedPrice;

}

De esta manera, incluso hemos protegido a la clase de recibir una llamada con cero descuento. Esa llamada significaría que la persona que llama piensa que hay un descuento, pero en realidad no hay ningún descuento en absoluto. Tal malentendido puede causar fácilmente un error.

En casos como este, prefiero no tener parámetros opcionales, sino forzar 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).

Usted puede encontrar análisis detallado de estos enlaces: Evitando Parámetros Opcionales y Evitando Parámetros Nulos

 0
Author: Zoran Horvat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-10 08:58:34

Mientras que son (supuestamente?) dos formas conceptualmente equivalentes disponibles para modelar su API desde cero, desafortunadamente tienen alguna diferencia sutil cuando necesita considerar la compatibilidad con versiones anteriores de tiempo de ejecución para sus clientes antiguos en la naturaleza. Mi colega (gracias Brent!) me señaló a este maravilloso post: Problemas de versionado con argumentos opcionales. Algunos citan de él:

La razón por la que se introdujeron parámetros opcionales en C # 4 en el el primer lugar fue para apoyar COM interop. Eso es. Y ahora, estamos conocer todas las implicaciones de este hecho. Si usted tiene una método con parámetros opcionales, nunca puede agregar una sobrecarga con parámetros opcionales adicionales por temor a causar un tiempo de compilación cambio de última hora. Y nunca se puede eliminar una sobrecarga existente, como esto siempre ha sido un cambio decisivo en el tiempo de ejecución. Necesitas más o menos para tratarlo como una interfaz. Su único recurso en este caso es escribir un nuevo método con un nuevo nombre. Así que tenga en cuenta esto si planea utilice argumentos opcionales en sus API.

 0
Author: RayLuo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-14 19:58:34

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 más generalmente, cuando las firmas de sus métodos habilitan patrones de uso que no tienen sentido, restringen el número de permutaciones de posibles llamadas. Por ejemplo, usando sobrecargas en lugar de opcionales (esta regla también es válida 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:

enum Match {
    Regex,
    Wildcard,
    ContainsString,
}

// Don't: This way, Enumerate() can be called in a way
//         which does not make sense:
IEnumerable<string> Enumerate(string searchPattern = null,
                              Match match = Match.Regex,
                              SearchOption searchOption = SearchOption.TopDirectoryOnly);

// Better: Provide only overloads which cannot be mis-used:
IEnumerable<string> Enumerate(SearchOption searchOption = SearchOption.TopDirectoryOnly);
IEnumerable<string> Enumerate(string searchPattern, Match match,
                              SearchOption searchOption = SearchOption.TopDirectoryOnly);
 0
Author: Sebastian Mach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-08 13:52:11