¿Por qué usar la palabra clave params?

336

Sé que esta es una pregunta básica, pero no pude encontrar una respuesta.

¿Por qué usarlo? Si escribe una función o un método que lo está utilizando, cuando lo elimine, el código seguirá funcionando perfectamente, 100% sin él. P.ej:

Con params:

static public int addTwoEach(params int[] args)
{
    int sum = 0;
    foreach (var item in args)
        sum += item + 2;
    return sum;
}

Sin params:

static public int addTwoEach(int[] args)
{
    int sum = 0;
    foreach (var item in args)
       sum += item + 2;
    return sum;
}
MasterMastic
fuente
62
El código del método en sí seguirá funcionando perfectamente ... el código de llamada bien podría no ...
Jon Skeet
77
palabra clave params significa parámetros OPCIONALES que se pueden pasar o no al Método. Una matriz sin palabra clave params significa que DEBE pasar el argumento de matriz al método.
Ailayna Entarria
El lenguaje Python implementa el mismo concepto tan dulcemente con un *parámetro prefijado asterisco ( ) como se menciona aquí .
RBT

Respuestas:

485

Conparams usted puede llamar a su método de esta manera:

addTwoEach(1, 2, 3, 4, 5);

Sin params, no puedes.

Además, puede llamar al método con una matriz como parámetro en ambos casos :

addTwoEach(new int[] { 1, 2, 3, 4, 5 });

Es decir, le paramspermite usar un acceso directo al llamar al método.

Sin relación, puede acortar drásticamente su método:

public static int addTwoEach(params int[] args)
{
    return args.Sum() + 2 * args.Length;
}
Konrad Rudolph
fuente
17
@Ken: es posible que deba importar el System.Linqespacio de nombres :)
Ranhiru Jude Cooray
49
O devuelva args.Sum (i => i + 2);
bornfromanegg
2
Sin embargo, la suma con un delegado aumenta la complejidad del código compilado, lo que podría ser menos eficaz. No es realmente relevante en esta situación particular, ya que no daría lugar a ningún cierre, pero vale la pena saber qué está haciendo realmente el compilador para tomar la mejor decisión.
Allen Clark Copeland Jr
2
También podría usar return args.Select(x => x + 2).Sum();
bbvg
1
Cuando agrega, paramsse bloquea para agregar argumentos de métodos adicionales sin interrumpir las llamadas o la resolución del método.
Sr. Young
93

El uso le paramspermite llamar a la función sin argumentos. Sin params:

static public int addTwoEach(int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // throws an error

Comparar con params:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // returns 0

En general, puede usar parámetros cuando el número de argumentos puede variar de 0 a infinito, y usar una matriz cuando el número de argumentos varía de 1 a infinito.

Vasya
fuente
13
En realidad, una matriz puede estar vacía. new int[0]. ¡espero que esto ayude! :)
vidstige
22

Le permite agregar tantos parámetros de tipo base en su llamada como desee.

addTwoEach(10, 2, 4, 6)

mientras que con la segunda forma tienes que usar una matriz como parámetro

addTwoEach(new int[] {10,2,4,6})
okrumnow
fuente
17

Un peligro con la paramspalabra clave es, si después de codificar las llamadas al método,

  1. alguien elimina accidental / intencionalmente uno o más parámetros requeridos de la firma del método, y
  2. uno o más parámetros requeridos inmediatamente antes del paramsparámetro antes del cambio de firma eran de tipo compatible con el paramsparámetro,

esas llamadas continuarán compilando con una o más expresiones previamente destinadas a los parámetros requeridos que se tratan como el paramsparámetro opcional . Me encontré con el peor de los casos posibles: el paramsparámetro era de tipo object[].

Esto es notable porque los desarrolladores están acostumbrados a que el compilador se golpee las muñecas con el escenario mucho, mucho más común, en el que los parámetros se eliminan de un método con todos los parámetros necesarios (porque el número de parámetros esperado cambiaría).

Para mí, no vale la pena el atajo. (Type)[]sin paramsfuncionará con 0 a infinito # de parámetros sin necesidad de anulaciones. El peor de los casos es que tendrá que agregar un , new (Type) [] {}a las llamadas donde no se aplique.

Por cierto, en mi humilde opinión, la práctica más segura (y más legible) es:

  1. pasar a través de Parámetros con nombre (que ahora podemos hacer incluso en C # ~ 2 décadas después de que pudiéramos en VB; P) (porque:

    1.1. es la única forma de garantizar la prevención de los valores no deseados pasados ​​a los Parámetros después del orden de los Parámetros, el Tipo de Compatible y / o el cambio de conteo después de que las Llamadas hayan sido codificadas,

    1.2. que reduce las posibilidades después de un cambio de parámetros significado, porque el probable nuevo nombre refleja el identificador nuevo significado es justo al lado del valor que se pasa a ella,

    1.3. evita tener que contar comas y saltar de Llamada a Firma para ver qué Expresión se pasa para qué Parámetro, y

    1.3.1. Por cierto, esta razón por sí sola debería ser suficiente (en términos de evitar frecuentes violaciones propensas a errores del Principio DRY solo para leer el código sin mencionar también modificarlo ), pero esta razón puede ser exponencialmente más importante si hay uno / Se pasan más expresiones que contienen comas, es decir, referencias de matriz multidimensional o llamadas de función de parámetros múltiples. En ese caso, ni siquiera podría usar (que incluso si pudiera, aún estaría agregando un paso adicional por parámetro por llamada al método) una función Buscar todas las ocurrencias en una selección en su editor para automatizar el conteo de comas para usted.

    1.4. si debe usar parámetros opcionales ( paramso no), le permite buscar llamadas donde se pasa un parámetro opcional particular (y, por lo tanto, lo más probable es que no sea o al menos tenga la posibilidad de no ser el valor predeterminado),

(NOTA: Las razones 1.2. Y 1.3. Pueden facilitar y reducir las posibilidades de error incluso al codificar las Llamadas iniciales, sin mencionar cuándo se deben leer y / o cambiar las Llamadas).

y

  1. hágalo UNO - PARÁMETRO - POR - LÍNEA para una mejor legibilidad (porque:

    2.1. está menos abarrotado y

    2.2. evita tener que desplazarse hacia la derecha y hacia atrás a la izquierda (y tener que hacerlo POR LÍNEA, ya que la mayoría de los mortales no pueden leer la parte izquierda de varias líneas, desplazarse hacia la derecha y leer la parte correcta)).

    2.3. es coherente con la "Mejor práctica" en la que ya hemos evolucionado para las declaraciones de asignación, porque cada parámetro aprobado es, en esencia, una declaración de asignación (asignación de un valor o referencia a una variable local). Al igual que aquellos que siguen la última "mejor práctica" en el estilo de codificación no soñarían con codificar múltiples declaraciones de asignación por línea, probablemente no deberíamos (y no lo haremos una vez que la "mejor práctica" alcance mi "genio"; P ) hazlo cuando pases los parámetros.

NOTAS :

  1. Pasar variables cuyos nombres reflejan los parámetros no ayuda cuando:

    1.1. está pasando constantes literales (es decir, un simple 0/1, falso / verdadero o nulo que incluso "" Mejores prácticas "" puede no requerir que use una constante con nombre y su propósito no puede deducirse fácilmente del nombre del método ),

    1.2. el Método es significativamente más bajo / más genérico que el Llamador, de modo que no querrá / no podrá nombrar sus Variables de la misma manera / similar a los Parámetros (o viceversa), o

    1.3. Usted es el reordenamiento / sustitución de los parámetros de la firma que pueden resultar en llamadas anteriores todavía Compilación porque los tipos suceden a seguir siendo compatibles.

  2. Tener una función de ajuste automático como VS solo elimina UNA (# 2.2) de las 8 razones que mencioné anteriormente. Antes de VS 2015, NO sangraba automáticamente (!?! Really, MS?!?) Lo que aumenta la gravedad de la razón # 2.1.

VS debe tener una opción que genere fragmentos de Llamada de método con Parámetros con nombre (uno por línea, por supuesto; P) y una opción de compilador que requiera Parámetros con nombre (similar en concepto a la Opción explícita en VB que, por cierto, el requisito de prolly se pensó una vez igualmente escandaloso, pero ahora es requerido por "" Mejores Prácticas ""). De hecho, "de vuelta en midía ";), en 1991, apenas meses después de mi carrera, incluso antes de usar (o incluso haber visto) un idioma con Parámetros con nombre, tenía el anti-sheeple /" solo porque puedes, no significa que debas " / no ciegamente "corte los extremos del asado" lo suficiente como para simularlo (usando comentarios en línea) sin haber visto a nadie hacerlo. No tener que usar Parámetros con nombre (así como otra sintaxis que guarde "'precioso'" pulsaciones de teclas del código fuente) es una reliquia de la era de la Tarjeta Punch cuando comenzó la mayoría de estas sintaxis. No hay excusa para eso con hardware moderno e IDE y software mucho más complejo donde la legibilidad es mucho, mucho, MUCHOmás importante. "El código se lee con mucha más frecuencia de lo que se escribe". Siempre y cuando no esté duplicando el código no actualizado automáticamente, es probable que cada pulsación de tecla guardada cueste exponencialmente más cuando alguien (incluso usted) intenta leerlo más tarde.

Tom
fuente
2
No entiendo. ¿Por qué no puedes simplemente exigir que haya al menos uno? Incluso sin parámetros no hay nada que te impida pasar nullo new object[0]como argumento.
Casey
Probablemente sea demasiado peligroso tener los parámetros requeridos antes del opcional (en caso de que uno o más de esos requisitos se eliminen después de codificar las llamadas). Esa puede ser la razón por la que nunca he visto los parámetros requeridos antes del parámetro opcional en el código de muestra en ningún documento sobre parámetros opcionales. Por cierto, en mi humilde opinión, la práctica más segura (y más legible) es pasar a través de parámetros con nombre (que ahora podemos hacer incluso en C # ~ 2 décadas después de que pudiéramos hacerlo en VB). VS debe tener una opción que genere fragmentos de llamadas a métodos con parámetros con nombre (y hacerlo 1 parámetro por línea).
Tom
No estoy muy seguro de lo que quieres decir. La única forma en que puede tener los parámetros necesarios y los opcionales es especificar primero todos los necesarios.
Casey
1
Ex. Declaro myMethodcomo void myMethod(int requiredInt, params int[] optionalInts). E / alguien códigos demás una / varias llamadas, es decir myMethod(1), myMethod(1, 21), myMethod(1, 21, 22). Me cambio myMethodpara ser void myMethod(params int[] optionalInts). Todas esas llamadas aún se compilarán sin errores a pesar de que sus primeros parámetros (los "1") claramente no fueron aprobados como el primer elemento del optionalIntsparámetro.
Tom
Oh. Bueno, está bien, en ese caso particular puede ser desaconsejado. No creo que haya ninguna razón para evitarlo si necesita una cadena y entradas de 0 a muchos o lo que sea.
Casey
10

No es necesario crear métodos de sobrecarga, solo use un método único con parámetros como se muestra a continuación

// Call params method with one to four integer constant parameters.
//
int sum0 = addTwoEach();
int sum1 = addTwoEach(1);
int sum2 = addTwoEach(1, 2);
int sum3 = addTwoEach(3, 3, 3);
int sum4 = addTwoEach(2, 2, 2, 2);
electricbah
fuente
Gracias por su aporte, pero no creo que este tipo de sobrecargas sea una solución, ya que con paramso sin nosotros simplemente pasaríamos un tipo de colección para cubrir cualquier cantidad de colecciones.
MasterMastic 01 de
Tiene razón en cierta medida, pero lo que lo hace genial es una sobrecarga sin parámetros de entrada, por ejemplo, int sum1 = addTwoEach ();
electricalbah
5

params También le permite llamar al método con un solo argumento.

private static int Foo(params int[] args) {
    int retVal = 0;
    Array.ForEach(args, (i) => retVal += i);
    return retVal;
}

es decir, en Foo(1);lugar de Foo(new int[] { 1 });. Puede ser útil para la taquigrafía en escenarios donde es posible que deba pasar un valor único en lugar de una matriz completa. Todavía se maneja de la misma manera en el método, pero da algunos dulces por llamar de esta manera.

David Anderson
fuente
5

Agregar la palabra clave params en sí muestra que puede pasar varios parámetros al llamar a ese método, lo que no es posible sin usarlo. Para ser más especifico:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

Cuando llame al método anterior, puede llamarlo de cualquiera de las siguientes maneras:

  1. addTwoEach()
  2. addTwoEach(1)
  3. addTwoEach(new int[]{ 1, 2, 3, 4 })

Pero cuando elimine la palabra clave params, solo la tercera forma de las formas dadas anteriormente funcionará bien. Para el primer y segundo caso, recibirá un error.

Ashwini
fuente
0

Hay que destacar algo más importante. Es mejor usarlo paramsporque es mejor para el rendimiento. Cuando llamas a un método con paramsargumento y no le pasas nada:

public void ExampleMethod(params string[] args)
{
// do some stuff
}

llamada:

ExampleMethod();

Luego, una nueva versión de .Net Framework hace esto (desde .Net Framework 4.6):

ExampleMethod(Array.Empty<string>());

Este Array.Emptymarco puede reutilizar este objeto más adelante, por lo que no es necesario realizar asignaciones redundantes. Estas asignaciones ocurrirán cuando llame a este método así:

 ExampleMethod(new string[] {});
Beniamin Makal
fuente
0

Puede sonar estúpido, pero Params no permite una matriz multidimensional. Mientras que puede pasar una matriz multidimensional a una función.

Manoj Kumar
fuente
0

Otro ejemplo

public IEnumerable<string> Tokenize(params string[] words)
{
  ...
}

var items = Tokenize(product.Name, product.FullName, product.Xyz)
skjagini
fuente