Eu estava assistindo a palestra de Anders sobre C # 4.0 e uma prévia do C # 5.0 , e isso me fez pensar sobre quando parâmetros opcionais estão disponíveis em C # qual será a maneira recomendada de declarar métodos que não precisam de todos os parâmetros especificados?
Por exemplo, algo como a FileStream
classe tem cerca de quinze construtores diferentes que podem ser divididos em 'famílias' lógicas, por exemplo, os que estão abaixo de uma string, os de an IntPtr
e os 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);
Parece-me que esse tipo de padrão poderia ser simplificado tendo três construtores, e usando parâmetros opcionais para aqueles que podem ser padronizados, o que tornaria as diferentes famílias de construtores mais distintas [nota: eu sei que essa mudança não será feita na BCL, estou falando hipoteticamente para esse tipo de situação].
O que você acha? A partir do C # 4.0, fará mais sentido transformar grupos intimamente relacionados de construtores e métodos em um único método com parâmetros opcionais ou há um bom motivo para continuar com o mecanismo tradicional de sobrecarga?
fonte
Quando uma sobrecarga de método normalmente executa a mesma coisa com um número diferente de argumentos, os padrões são usados.
Quando uma sobrecarga de método executa uma função de maneira diferente com base em seus parâmetros, a sobrecarga continuará a ser usada.
Eu usei opcional nos meus dias de VB6 e desde então perdi, ele reduzirá muito a duplicação de comentários XML em C #.
fonte
Tenho usado Delphi com parâmetros opcionais desde sempre. Em vez disso, mudei para o uso de sobrecargas.
Porque quando você criar mais sobrecargas, invariavelmente entrará em conflito com um formulário de parâmetro opcional e, de qualquer maneira, terá que convertê-los em não opcionais.
E eu gosto da noção de que geralmente há um super método e o resto são invólucros mais simples desse.
fonte
Foo(A, B, C)
requerFoo(A)
,Foo(B)
,Foo(C)
,Foo(A, B)
,Foo(A, C)
,Foo(B, C)
.Definitivamente estarei usando o recurso de parâmetros opcionais do 4.0. Livra-se do ridículo ...
... e coloca os valores exatamente onde o chamador pode vê-los ...
Muito mais simples e menos sujeito a erros. Na verdade, eu vi isso como um bug no caso de sobrecarga ...
Ainda não joguei com o compilador 4.0, mas não ficaria chocado em saber que o compilador simplesmente emite sobrecargas para você.
fonte
Os parâmetros opcionais são essencialmente um pedaço de metadados que direciona um compilador que está processando uma chamada de método para inserir os padrões apropriados no site da chamada. Por outro lado, as sobrecargas fornecem um meio pelo qual um compilador pode selecionar um de vários métodos, alguns dos quais podem fornecer os próprios valores padrão. Observe que se alguém tentar chamar um método que especifica parâmetros opcionais de código escrito em uma linguagem que não os suporta, o compilador exigirá que os parâmetros "opcionais" sejam especificados, mas desde que chamar um método sem especificar um parâmetro opcional é equivalente a chamá-lo com um parâmetro igual ao valor padrão, não há obstáculo para que essas linguagens chamem esses métodos.
Uma consequência significativa da associação de parâmetros opcionais no site da chamada é que eles receberão valores com base na versão do código-alvo que está disponível para o compilador. Se um assembly
Foo
tiver um métodoBoo(int)
com valor padrão 5 e o assemblyBar
contiver uma chamada paraFoo.Boo()
, o compilador irá processá-lo como umFoo.Boo(5)
. Se o valor padrão for alterado para 6 e o assemblyFoo
for recompilado,Bar
continuará a chamar, aFoo.Boo(5)
menos ou até que seja recompilado com essa nova versão doFoo
. Portanto, deve-se evitar o uso de parâmetros opcionais para coisas que podem mudar.fonte
void Foo(int value) … void Foo() { Foo(42); }
. Do lado de fora, o chamador não sabe qual valor padrão é usado, nem quando pode ser alterado; seria preciso monitorar a documentação escrita para isso. Os valores padrão para parâmetros opcionais podem ser vistos apenas como: documentação no código qual é o valor padrão.Pode-se argumentar se argumentos opcionais ou sobrecargas devem ser usados ou não, mas o mais importante, cada um tem sua própria área onde são insubstituíveis.
Argumentos opcionais, quando usados em combinação com argumentos nomeados, são extremamente úteis quando combinados com algumas listas de argumentos longos com todos os opcionais de chamadas COM.
Sobrecargas são extremamente úteis quando o método é capaz de operar em muitos tipos de argumentos diferentes (apenas um dos exemplos) e faz castings internamente, por exemplo; você apenas o alimenta com qualquer tipo de dado que faça sentido (que seja aceito por alguma sobrecarga existente). Não consigo superar isso com argumentos opcionais.
fonte
Estou ansioso para os parâmetros opcionais porque mantém o que os padrões estão mais próximos do método. Portanto, em vez de dezenas de linhas para as sobrecargas que apenas chamam o método "expandido", você apenas define o método uma vez e pode ver como os parâmetros opcionais são padronizados na assinatura do método. Prefiro olhar para:
Em vez disso:
Obviamente, este exemplo é muito simples, mas no caso do OP com 5 sobrecargas, as coisas podem ficar lotadas rapidamente.
fonte
Um dos meus aspectos favoritos dos parâmetros opcionais é que você vê o que acontece com seus parâmetros se não os fornecer, mesmo sem ir para a definição do método. O Visual Studio simplesmente mostrará o valor padrão do parâmetro quando você digitar o nome do método. Com um método de sobrecarga, você fica preso em ler a documentação (se ainda estiver disponível) ou em navegar diretamente para a definição do método (se disponível) e para o método que a sobrecarga envolve.
Em particular: o esforço de documentação pode aumentar rapidamente com a quantidade de sobrecargas, e você provavelmente terminará copiando comentários já existentes das sobrecargas existentes. Isso é muito irritante, pois não produz nenhum valor e quebra o princípio DRY ). Por outro lado, com um parâmetro opcional, há exatamente um lugar onde todos os parâmetros são documentados e você vê seu significado, bem como seus valores padrão durante a digitação.
Por último, mas não menos importante, se você é o consumidor de uma API, pode nem mesmo ter a opção de inspecionar os detalhes de implementação (se você não tiver o código-fonte) e, portanto, não terá chance de ver para qual super método os sobrecarregados estão envolvendo. Assim, você fica preso com a leitura do documento e esperando que todos os valores padrão estejam listados lá, mas nem sempre é o caso.
Claro, esta não é uma resposta que trate de todos os aspectos, mas acho que adiciona uma que não foi abordada até agora.
fonte
Embora sejam (supostamente?) Duas maneiras conceitualmente equivalentes disponíveis para você modelar sua API do zero, eles infelizmente têm algumas diferenças sutis quando você precisa considerar a compatibilidade com versões anteriores do tempo de execução para seus clientes antigos. Meu colega (obrigado, Brent!) Me indicou este post maravilhoso: Problemas de versionamento com argumentos opcionais . Algumas citações dele:
fonte
Uma ressalva dos parâmetros opcionais é o controle de versão, em que uma refatoração tem consequências indesejadas. Um exemplo:
Código inicial
Suponha que este seja um dos muitos chamadores do método acima:
Aqui, o evento não é silencioso e é tratado como crítico.
Agora, digamos que, após uma refatoração, descobrimos que todos os erros solicitam ao usuário de qualquer maneira, portanto, não precisamos mais do sinalizador de silêncio. Então, nós o removemos.
Depois de refatorar
A chamada anterior ainda compila, e digamos que ela passe pelo refatorador inalterada:
Agora
false
terá um efeito indesejado, o evento não será mais tratado como crítico.Isso pode resultar em um defeito sutil, já que não haverá erro de compilação ou tempo de execução (ao contrário de algumas outras advertências de opcionais, como este ou este ).
Observe que existem muitas formas desse mesmo problema. Um outro formulário é descrito aqui .
Note também que estritamente usando parâmetros nomeados ao chamar o método vai evitar o problema, como assim:
HandleError("Disk is full", silent:false)
. No entanto, pode não ser prático presumir que todos os outros desenvolvedores (ou usuários de uma API pública) o farão.Por esses motivos, eu evitaria usar parâmetros opcionais em uma API pública (ou até mesmo um método público, se pudesse ser usado amplamente), a menos que haja outras considerações convincentes.
fonte
Ambos os parâmetros opcionais e a sobrecarga do método têm suas próprias vantagens ou desvantagens. Depende de sua preferência para escolher entre eles.
Parâmetro opcional: disponível apenas em .Net 4.0. parâmetro opcional reduz o tamanho do código. Você não pode definir o parâmetro out e ref
métodos sobrecarregados: Você pode definir os parâmetros Out e ref. O tamanho do código aumentará, mas os métodos sobrecarregados são fáceis de entender.
fonte
Em muitos casos, parâmetros opcionais são usados para alternar a execução. Por exemplo:
O parâmetro de desconto aqui é usado para alimentar a instrução if-then-else. Existe o polimorfismo que não foi reconhecido e então foi implementado como uma instrução if-then-else. Nesses casos, é muito melhor dividir os dois fluxos de controle em dois métodos independentes:
Dessa forma, protegemos até mesmo a turma de receber uma chamada com desconto zero. Essa ligação significaria que o chamador pensa que existe o desconto, mas na verdade não existe desconto algum. Esse mal-entendido pode facilmente causar um bug.
Em casos como este, prefiro não ter parâmetros opcionais, mas forçar o chamador a selecionar explicitamente o cenário de execução que se adapta à sua situação atual.
A situação é muito semelhante a ter parâmetros que podem ser nulos. Isso é igualmente má ideia quando a implementação ferve para declarações como
if (x == null)
.Você pode encontrar análises detalhadas nestes links: Evitando Parâmetros Opcionais e Evitando Parâmetros Nulos
fonte
Para adicionar um acéfalo ao usar uma sobrecarga em vez de opcionais:
Sempre que você tiver vários parâmetros que só façam sentido juntos, não introduza opcionais neles.
Ou, de forma mais geral, sempre que as assinaturas de método permitem padrões de uso que não fazem sentido, restrinja o número de permutações de chamadas possíveis. Por exemplo, usando sobrecargas em vez de opcionais (essa regra também é válida quando você tem vários parâmetros do mesmo tipo de dados, a propósito; aqui, dispositivos como métodos de fábrica ou tipos de dados personalizados podem ajudar).
Exemplo:
fonte