Pergunta

Eu estava assistindo Anders' conversa sobre C # 4.0 e sneak preview do C # 5.0 , e isso me fez pensar sobre quando parâmetros opcionais estão disponíveis em C # o que vai ser a maneira recomendada para declarar métodos que não precisam de todos os parâmetros especificados?

Por exemplo algo como a classe FileStream tem cerca de quinze construtores diferentes que podem ser divididos em 'famílias' lógicas por exemplo os abaixo de uma corda, os de um IntPtr e os de um 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 este tipo de padrão pode ser simplificado por ter três construtores em vez disso, e usando parâmetros opcionais para os que podem ser inadimplentes, o que tornaria as diferentes famílias de construtores mais distintas [Nota: Eu sei que esta mudança não será feita na BCL, eu estou falando hipoteticamente para este tipo de situação].

O que você acha? De C # 4.0 será que vai fazer mais sentido para fazer grupos intimamente relacionados de construtores e métodos um único método com parâmetros opcionais, ou há uma boa razão para ficar com o mecanismo de muitos sobrecarga tradicional?

Foi útil?

Solução

Eu considere o seguinte:

  • Você precisa de seu código para ser usado a partir de linguagens que não suportam parâmetros opcionais? Se assim for, considere incluindo as sobrecargas.
  • Você tem membros em sua equipe que violentamente se opõem parâmetros opcionais? (Às vezes é mais fácil viver com uma decisão que você não gosta do que discutir o caso.)
  • Você está confiante de que seus padrões não vai mudar entre versões do seu código, ou se eles poderiam, se o seu interlocutor ficar bem com isso?

Eu não tenho verificado como os padrões estão indo para o trabalho, mas eu supor que os valores padrão será cozido no código de chamada, da mesma forma como referências a campos const. Isso é geralmente bem - muda para um valor padrão é de qualquer maneira bastante significativo -. Mas essas são as coisas a considerar

Outras dicas

Quando uma sobrecarga método normalmente realiza a mesma coisa com um número diferente de argumentos, em seguida, irá ser utilizados padrões.

Quando uma sobrecarga do método executa uma função de modo diferente com base nos seus parâmetros então sobrecarga continuará a ser utilizado.

Eu costumava voltar opcional em meus dias VB6 e desde então perdeu, ele vai reduzir um monte de comentário duplicação XML em C #.

Estou usando o Delphi, com parâmetros opcionais, para sempre. i passamos a usar sobrecargas vez.

Porque quando você vai para criar mais sobrecargas, você invariavelmente confict com uma forma parâmetro opcional; e então você vai ter que convertê-los em qualquer maneira não opcional.

E eu gosto da idéia de que há geralmente um Super método, eo resto são invólucros mais simples em torno de que um.

Eu definitivamente estarei usando os parâmetros opcionais apresentam de 4,0. Ele se livrar do 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
}

... e coloca os valores direita, onde o chamador pode vê-los ...

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

Muito mais simples e muito menos propenso erro. Eu realmente vi isso como um bug no caso de sobrecarga ...

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

Eu não tenho jogado com o compilador 4.0, mas eu não ficaria chocado ao saber que o compilador simplesmente emite as sobrecargas para você.

Parâmetros opcionais são essencialmente um pedaço de metadados que dirige um compilador que está processando uma chamada de método para inserir os padrões apropriados no site da chamada. Por outro lado, sobrecargas fornecem um meio pelo qual um compilador pode selecionar um de uma série de métodos, alguns dos quais podem fornecer valores de padrões próprios. Nota que, se alguém tenta chamar um método que especifica parâmetros opcionais a partir do código escrito em uma linguagem que não apoiá-los, o compilador irá exigir a indicação dos parâmetros "opcionais", 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á nenhum obstáculo para tais idiomas chamando tais métodos.

Uma consequência significativa de ligação de parâmetros opcionais no site da chamada é que eles vão ser atribuídos valores baseados na versão do código-alvo que está disponível para o compilador. Se um Foo montagem tem um Boo(int) método com um valor padrão de 5, e de montagem Bar contém uma chamada para Foo.Boo(), o compilador irá processar que como um Foo.Boo(5). Se o valor padrão é alterada para 6 e montagem Foo é recompilado, Bar continuará a chamar Foo.Boo(5) a menos ou até que seja recompilados com que a nova versão do Foo. Deve-se, portanto, evitar o uso de parâmetros opcionais para as coisas que podem mudar.

Estou ansioso para parâmetros opcionais, pois mantém o que os padrões estão mais próximos do método. Então, ao invés de dezenas de linhas para as sobrecargas que apenas chamar o método "expandido", que você acabou de definir o método de uma vez e você pode ver o que o opcional parâmetros padrão para na assinatura do método. Eu prefiro olhar:

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

Em vez disso:

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 exemplo é muito simples, mas o caso do OP com 5 sobrecargas, as coisas podem ficar lotada rápida real.

Pode-se argumentar se argumentos ou sobrecargas opcionais deve ser usado ou não, mas o mais importante, cada um tem sua própria área onde eles são insubstituíveis.

Os argumentos opcionais, quando usados ??em combinação com argumentos nomeados, são extremamente útil quando combinado com alguns longas-argumento listas-com-todos-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 peças fundidas internamente, por exemplo; você apenas alimentá-lo com qualquer tipo de dados que faz sentido (que é aceito por alguns sobrecarga existente). Que não pode vencer com argumentos opcionais.

Um dos meus favoritos aspectos de parâmetros opcionais é que você vê o que acontece com seus parâmetros se você não fornecê-los, mesmo sem ir para a definição do método. Visual Studio irá simplesmente mostrar-lhe o valor padrão para o parâmetro quando você digita o nome do método. Com uma sobrecarga de método você está preso com tanto ler a documentação (se ainda disponível) ou com a navegação diretamente para a definição do método (se disponível) e para o método que os envoltórios de sobrecarga.

Em particular: o esforço de documentação pode aumentar rapidamente com a quantidade de sobrecargas, e você provavelmente vai acabar copiando comentários já existentes a partir das sobrecargas existentes. Isso é muito chato, uma vez que não produz qualquer valor e quebra o DRY-princípio ). Por outro lado, com um parâmetro opcional há exatamente um lugar , onde todos os parâmetros são documentados e você vê o seu significado, bem como seus valores padrão ao digitar.

Por último, mas não menos importante, se você é o consumidor de uma API que você não pode mesmo ter a opção de inspecionar os detalhes de implementação (se você não tem o código-fonte) e, portanto, não têm chance de ver a que Super método os sobrecarregados são embrulho. Assim, você está preso com a leitura do doc e esperando que todos os valores padrão estão listados lá, mas isso nem sempre é o caso.

Claro, isso não é uma resposta que lida com todos os aspectos, mas eu acho que acrescenta um que não tenha sido coberto até agora.

Uma ressalva de parâmetros opcionais é versionamento, onde um refactor tem consequências não intencionais. Um exemplo:

código inicial

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

Suponha que este é um dos muitos chamadores do método acima:

HandleError("Disk is full", false);

Aqui, o evento não é silencioso e é tratado como crítica.

Agora vamos dizer que depois de um refactor descobrimos que todos os erros prompt de usuário de qualquer maneira, por isso não precisa mais da bandeira em silêncio. Então, nós removê-lo.

Após refactor

O ex-chamada ainda compila, e vamos dizer que desliza através do refactor inalterada:

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

...

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

Agora false terá um efeito não intencional, o evento não será mais tratada como crítica.

Isso poderia resultar em um defeito sutil, já que não haverá nenhuma compilação ou erro de execução (ao contrário de algumas outras advertências de opcionais, tais como este ou este ).

Note que existem muitas formas de esse mesmo problema. Uma outra forma é descrita 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, ele não pode ser prático para assumir que todos os outros desenvolvedores (ou utilizadores de uma API pública) vai fazê-lo.

Por estas razões, seria evitar o uso de parâmetros opcionais em uma API pública (ou mesmo um método público se ele pode ser amplamente utilizado) a menos que haja outras considerações convincentes.

Tanto o parâmetro opcional, a sobrecarga de método tem lá própria vantagem ou disadvantage.it depende da sua preferência para escolher entre elas.

parâmetro opcional: disponível apenas em .Net 4.0. parâmetro opcional reduzir o tamanho do código. Você não pode definir para fora e ref parâmetro

métodos sobrecarregados: Você pode definir para fora e parâmetros ref. o tamanho do código vai aumentar, mas método sobrecarregado são fáceis de entender.

Em muitos casos, os parâmetros opcionais são usados ??para alternar execução. Por exemplo:

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

    decimal basePrice = CalculateBasePrice(productName);

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

parâmetro de desconto aqui é usado para alimentar a declaração if-then-else. Há o polimorfismo que não foi reconhecido, e depois foi implementado como uma instrução if-then-else. Em tais casos, é muito melhor para dividir os dois controle flui para dois métodos independentes:

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;

}

Desta forma, temos ainda protegidos da classe de receber uma chamada com desconto zero. Essa chamada significaria que o chamador pensa que há o desconto, mas na verdade não há desconto em tudo. Tal engano pode facilmente causar um erro.

Em casos como este, eu preferiria não ter parâmetros opcionais, mas para forçar o chamador explicitamente selecionar o cenário de execução que corresponde à sua situação actual.

A situação é muito semelhante ao que têm parâmetros que podem ser nulos. Que é igualmente má ideia quando a implementação ferve a declarações como if (x == null).

Você pode encontrar uma análise detalhada sobre estes links: evitando parâmetros opcionais e evitando parâmetros nulos

Enquanto eles são (supostamente?) Duas maneiras conceitualmente equivalentes disponíveis para que você possa modelar sua API a partir do zero, eles infelizmente, têm alguma diferença sutil quando você precisa considerar a compatibilidade de tempo de execução para trás para seus antigos clientes em estado selvagem. O meu colega (graças Brent!) Apontou-me a esta pós maravilhosa: Versionando problemas com argumentos opcionais . Alguns citam a partir dele:

A razão que os parâmetros opcionais foram introduzidas para C # 4 na primeiro lugar foi para interoperabilidade suporte COM. É isso aí. E agora, estamos aprendendo sobre as implicações deste fato. Se você tem um método com parâmetros opcionais, você nunca pode adicionar uma sobrecarga com parâmetros adicionais opcionais por medo de causar um tempo de compilação alteração de quebra. E você nunca pode remover uma sobrecarga existente, como esta sempre foi uma mudança de tempo de execução de ruptura. Você praticamente necessidade tratá-lo como uma interface. Seu único recurso, neste caso, é escrever um novo método com um novo nome. Então, estar ciente disto se você pretende usar argumentos opcionais em suas APIs.

Para adicionar um acéfalo quando usar uma sobrecarga em vez de opcionais:

Sempre que você tem um número de parâmetros que só fazem sentido juntos, não introduzem opcionais sobre eles.

ou, mais geralmente, sempre que o seu método de assinaturas permitem padrões de uso que não fazem sentido, limitar o número de permutações de possíveis chamadas. Por exemplo, usando sobrecargas em vez de opcionais. (Esta regra também se aplica 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 personalizado pode ajudar)

Exemplo:

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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top