Por que usar a palavra-chave params?

336

Sei que essa é uma pergunta básica, mas não consegui encontrar uma resposta.

Por que usar isso? se você escrever uma função ou um método que a esteja usando, ao removê-lo, o código ainda funcionará perfeitamente, 100% sem ele. Por exemplo:

Com params:

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

Sem parâmetros:

static public int addTwoEach(int[] args)
{
    int sum = 0;
    foreach (var item in args)
       sum += item + 2;
    return sum;
}
MasterMastic
fonte
62
O código do próprio método ainda irá funcionar perfeitamente ... o chamado código pode muito bem não ...
Jon Skeet
7
palavra-chave params significa parâmetros OPCIONAIS que podem ser passados ​​ou não ao método. Uma matriz com a palavra-chave sem parâmetros significa que você DEVE passar o argumento da matriz para o método.
Ailayna Entarria
A linguagem Python implementa o mesmo conceito tão docemente com um *parâmetro prefixado asterisk ( ) como mencionado aqui .
RBT 28/07

Respostas:

485

Comparams você pode chamar seu método assim:

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

Sem params, você não pode.

Além disso, você pode chamar o método com uma matriz como parâmetro nos dois casos :

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

Ou seja, paramspermite que você use um atalho ao chamar o método.

Independentemente, você pode reduzir drasticamente seu método:

public static int addTwoEach(params int[] args)
{
    return args.Sum() + 2 * args.Length;
}
Konrad Rudolph
fonte
17
@ Ken: Você pode precisar importar o System.Linqnamespace :)
Ranhiru Jude Cooray
49
Ou retorne args.Sum (i => i + 2);
bornfromanegg
2
A soma com um delegado, no entanto, aumenta a complexidade do código compilado, que pode ser menos eficiente. Não é realmente relevante nessa situação específica, pois não resultaria em nenhum fechamento, mas vale a pena saber o que o compilador está realmente fazendo para fazer a melhor escolha.
Allen Clark Copeland Jr
2
Você também pode usar return args.Select(x => x + 2).Sum();
bbvg
11
Quando você adiciona paramsvocê bloquear -se fora de acrescentar argumentos de método adicionais sem quebrar o seu interlocutor ou resolução método.
Sr. Young
93

Usar paramspermite chamar a função sem argumentos. Sem params:

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

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

    return sum;
}

addtwoEach(); // throws an error

Compare com params:

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

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

    return sum;
}

addtwoEach(); // returns 0

Geralmente, você pode usar parâmetros quando o número de argumentos puder variar de 0 ao infinito e usar uma matriz quando o número de argumentos variar de 1 ao infinito.

Vasya
fonte
13
na verdade, uma matriz pode estar vazia. new int[0]. espero que isto ajude! :)
vidstige
22

Ele permite que você adicione quantos parâmetros do tipo base em sua chamada desejar.

addTwoEach(10, 2, 4, 6)

enquanto que com o segundo formulário você deve usar uma matriz como parâmetro

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

Um perigo da paramspalavra-chave é que, após a codificação das chamadas para o método,

  1. alguém acidentalmente / intencionalmente remove um / mais requeridos parâmetros da assinatura de método e
  2. um / mais parâmetros necessários imediatamente antes do paramsparâmetro antes da alteração da assinatura eram compatíveis com o tipo do paramsparâmetro,

essas chamadas continuarão sendo compiladas com uma / mais expressões anteriormente destinadas aos parâmetros necessários, sendo tratadas como o paramsparâmetro opcional . Acabei de encontrar o pior caso possível disso: o paramsparâmetro era do tipoobject[] .

Isso é digno de nota porque os desenvolvedores estão acostumados com o compilador batendo os pulsos com o cenário muito mais comum em que os parâmetros são removidos de um método com todos os requisitos Parâmetros (porque o número de Parâmetros esperados mudaria).

Para mim, não vale o atalho. (Type)[]sem paramsfuncionará com 0 ao infinito # de Parâmetros sem precisar de Substituições. O pior caso é que você terá que adicionar um, new (Type) [] {} a Chamadas onde não se aplica.

Btw, imho, a prática mais segura (e mais legível) é:

  1. passar por parâmetros nomeados (que agora podemos fazer mesmo em C # ~ 2 décadas depois poderíamos em VB; P) (porque:

    1.1 é a única maneira de garantir prevenção de valores não intencionais transmitidos aos parâmetros após a ordem dos parâmetros, tipo compatível e / ou alteração de contagem após a codificação das chamadas,

    1.2 que reduz essas chances após uma mudança de significado de parâmetros, porque o novo nome de identificador provavelmente refletindo o novo significado é bem próximo ao valor que está sendo passado para ele,

    1.3 evita ter que contar vírgulas e ir e voltar de Chamada para Assinatura para ver qual Expressão está sendo transmitida para qual Parâmetro e

    1.3.1 A propósito, esse motivo por si só deve ser suficiente (em termos de evitar violações frequentes do Princípio DRY, propensas a erros, apenas para ler o código, para não mencionar também modificá- lo), mas esse motivo pode ser exponencialmente mais importante se houver um / mais Expressões sendo passadas que contêm vírgulas, ou seja, referências de matriz multidimensional ou chamadas de função de vários parâmetros. Nesse caso, você não poderia nem usar (que, mesmo que pudesse, ainda estaria adicionando uma etapa extra por parâmetro por chamada de método) a Encontrar todas as ocorrências em um recurso de seleção no seu editor para automatizar a contagem de vírgulas para você.

    1.4 se você deve usar Parâmetros Opcionais ( paramsou não), ele permite procurar Chamadas nas quais um Parâmetro Opcional específico é passado (e, portanto, provavelmente não é ou pelo menos tem a possibilidade de não ser o Valor Padrão),

(NOTA: Os motivos 1.2. E 1.3. Podem facilitar e reduzir as chances de erro, mesmo na codificação das chamadas iniciais, sem mencionar quando as chamadas precisam ser lidas e / ou alteradas.)

e

  1. faça isso UM PARÂMETRO - POR LINHA para melhor legibilidade (porque:

    2.1 é menos confuso, e

    2.2 evita ter que rolar para a direita e para trás para a esquerda (e para fazê-lo por linha, pois a maioria dos mortais não consegue ler a parte esquerda de várias linhas, role para a direita e leia a parte direita)).

    2.3 é consistente com a "Melhor prática" em que já desenvolvemos as declarações de atribuição, porque cada parâmetro passado é essencialmente uma declaração de atribuição (atribuindo um valor ou referência a uma variável local). Assim como aqueles que seguem as mais recentes "melhores práticas" no estilo de codificação não sonhariam em codificar várias declarações de atribuição por linha, provavelmente não deveríamos (e não faremos uma vez que "as melhores práticas" alcancem meu "gênio"; P ) faça isso ao passar parâmetros.

NOTAS :

  1. Passar variáveis ​​cujos nomes espelham os parâmetros não ajuda quando:

    1.1 você está passando constantes constantes literais (ou seja, um simples 0/1, falso / verdadeiro ou nulo, para o qual até mesmo as '' Melhores Práticas '' podem não exigir o uso de uma Constante Nomeada e seu objetivo não pode ser facilmente deduzido do nome do Método ),

    1.2 o método é significativamente mais baixo / mais genérico do que o chamador, de modo que você não gostaria / seria capaz de nomear suas variáveis ​​da mesma / semelhante aos parâmetros (ou vice-versa) ou

    1.3 você está re-ordenação / substituição Parâmetros na assinatura que pode resultar em chamadas anteriores ainda Compilando porque os tipos de acontecer ainda ser compatível.

  2. Ter um recurso de quebra automática, como o VS, elimina apenas UM (# 2.2) dos 8 motivos mencionados acima. Antes do VS 2015, ele NÃO fazia recuo automático (!?! Realmente, MS?!?), O que aumenta a gravidade do motivo # 2.1.

O VS deve ter uma opção que gere trechos de chamada de método com parâmetros nomeados (um por linha, é claro; P) e uma opção de compilador que requer parâmetros nomeados (semelhante no conceito à opção explícita no VB, que, por outro lado, o requisito de era prolly uma vez pensado igualmente ultrajante, mas agora é necessário pelas "'Melhores práticas'"). De fato, "de volta na minhaday ";), em 1991, apenas meses depois da minha carreira, mesmo antes de eu estar usando (ou mesmo visto) uma linguagem com parâmetros nomeados, eu tinha o anti-sheeple /" só porque você pode, não significa que deveria " / não cegamente "corta as extremidades do assado" o suficiente para simulá-lo (usando comentários em linha) sem ter visto alguém fazer isso. Não é necessário usar parâmetros nomeados (assim como outras sintaxes que salvam "'precioso'" pressionamentos de código-fonte) é uma relíquia da era do Punch Card, quando a maioria dessas sintaxes começou. Não há desculpa para isso com hardware e IDE modernos e software muito mais complexo, onde a legibilidade é muito, Muito, MUITOmais importante. "O código é lido com muito mais frequência do que está escrito". Desde que você não esteja duplicando o código não atualizado automaticamente, cada pressionamento de tecla salvo provavelmente custará exponencialmente mais quando alguém (até você) estiver tentando lê-lo mais tarde.

Tom
fonte
2
Eu não entendo Por que você não pode simplesmente impor que exista pelo menos um? Mesmo sem parâmetros, não há nada para impedi-lo de passar nullou new object[0]como argumento.
Casey #
Provavelmente, é muito perigoso ter parâmetros necessários antes do opcional (caso um ou mais deles sejam removidos após a codificação das chamadas). Talvez seja por isso que nunca vi parâmetros obrigatórios antes do parâmetro opcional no código de exemplo em nenhum documento sobre parâmetros opcionais. Entre, imho, a prática mais segura (e mais legível) é passar por parâmetros nomeados (o que podemos fazer agora mesmo em C # ~ 2 décadas depois que pudemos no VB). O VS deve ter uma opção que gere trechos de chamada de método com parâmetros nomeados (e faça 1 parâmetro por linha).
Tom
Não tenho muita certeza do que você quer dizer. A única maneira de você ter parâmetros necessários e opcionais é especificar primeiro todos os necessários.
Casey
11
Ex. Eu declaro myMethodcomo void myMethod(int requiredInt, params int[] optionalInts). Alguém códigos mais eu / um / mais chamadas, ou seja myMethod(1), myMethod(1, 21), myMethod(1, 21, 22). Eu mudo myMethodpara ser void myMethod(params int[] optionalInts). Todas essas chamadas ainda serão compiladas sem erros, mesmo que seus primeiros parâmetros (os "1") não tenham sido destinados a serem passados ​​como o primeiro elemento do optionalIntsParâmetro.
Tom
Oh. Bem, ok, nesse caso em particular, pode ser desaconselhável. Eu não acho que haja qualquer razão para evitá-lo se você precisar de uma string e 0 a muitos ints ou o que quer.
Casey
10

Não há necessidade de criar métodos de sobrecarga, basta usar um único método com parâmetros, como mostrado abaixo

// 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
fonte
Obrigado pela sua contribuição, mas não acho que esse tipo de sobrecarga seria uma solução, já que com paramsou sem passaríamos apenas um tipo de coleção para cobrir qualquer contagem de coleções.
MasterMastic
Você está certo em certa medida, mas o que o torna legal é uma sobrecarga sem parâmetro de entrada, por exemplo, int sum1 = addTwoEach ();
electricalbah
5

params também permite que você chame o método com um único argumento.

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

ou seja, em Foo(1);vez de Foo(new int[] { 1 });. Pode ser útil para abreviar em cenários em que talvez você precise transmitir um único valor em vez de uma matriz inteira. Ele ainda é tratado da mesma maneira no método, mas fornece alguns doces para chamar dessa maneira.

David Anderson
fonte
5

A adição da própria palavra-chave params mostra que você pode passar vários números de parâmetros enquanto chama o método que não é possível sem usá-lo. Para ser mais específico:

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

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

    return sum;
}

Quando você chamar o método acima, poderá chamá-lo de qualquer uma das seguintes maneiras:

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

Mas quando você removerá a palavra-chave params, apenas a terceira maneira das formas acima indicadas funcionará bem. Para o 1º e o 2º caso, você receberá um erro.

Ashwini
fonte
0

Mais uma coisa importante precisa ser destacada. É melhor usar paramsporque é melhor para o desempenho. Quando você chama um método com paramsargumento e passa a ele nada:

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

ligar:

ExampleMethod();

Em seguida, uma nova versão do .Net Framework faz isso (do .Net Framework 4.6):

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

Esse Array.Emptyobjeto pode ser reutilizado pela estrutura posteriormente, portanto, não há necessidade de fazer alocações redundantes. Essas alocações ocorrerão quando você chamar esse método da seguinte maneira:

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

Pode parecer estúpido, mas o Params não permite uma matriz multidimensional. Considerando que você pode passar uma matriz multidimensional para uma função.

Manoj Kumar
fonte
0

Outro exemplo

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

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