Design de parâmetro

Observação

Este conteúdo é reimpresso com permissão da Pearson Education, Inc. de Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition. Essa edição foi publicada em 2008 e, desde então, o livro foi totalmente revisado na terceira edição. Algumas das informações nesta página podem estar desatualizadas.

Esta seção oferece diretrizes amplas sobre o design de parâmetros, incluindo seções com diretrizes para verificar argumentos. Além disso, você deve consultar as diretrizes descritas em Parâmetros de nomenclatura.

✔️ Use o tipo de parâmetro menos derivado que fornece a funcionalidade exigida pelo membro.

Por exemplo, suponha que você queira criar um método que enumere uma coleção e imprima cada item no console. Esse método deve usar IEnumerable como o parâmetro, não ArrayList ou IList, por exemplo.

❌ NÃO use parâmetros reservados.

Se mais entradas para um membro forem necessárias em alguma versão futura, uma nova sobrecarga poderá ser adicionada.

❌ NÃO tenha métodos expostos publicamente que usem ponteiros, matrizes de ponteiros ou matrizes multidimensionais como parâmetros.

É relativamente difícil usar ponteiros e matrizes multidimensionais corretamente. Em quase todos os casos, as APIs podem ser reprojetadas para evitar o uso desses tipos como parâmetros.

✔️ Coloque todos os parâmetros out seguindo todos os valores e parâmetros ref (excluindo matrizes de parâmetros), mesmo que isso resulte em uma inconsistência na ordenação de parâmetros entre sobrecargas (consulte Sobrecarga de membro).

Os parâmetros out podem ser vistos como valores retornados extra e o agrupamento deles facilita o entendimento da assinatura do método.

✔️ Seja consistente na nomeação dos parâmetros ao substituir membros ou implementar membros da interface.

Isso comunica melhor a relação entre os métodos.

Escolhendo entre parâmetros Enum e Boolean

✔️ Use enumerações se um membro tiver dois ou mais parâmetros boolianos.

❌ NÃO use boolianos, a menos que você tenha certeza absoluta de que nunca haverá necessidade de mais de dois valores.

As enumerações dão a você algum espaço para adição futura de valores, mas você deve estar ciente de todas as implicações da adição de valores a enumerações, que estão descritas em Design de enumeração.

✔️ Considere o uso de boolianos para parâmetros de construtor que são realmente valores de dois estados e são usados para inicializar propriedades booleanas.

Validando argumentos

✔️ Valide argumentos passados para membros públicos, protegidos ou explicitamente implementados. Gere System.ArgumentException ou uma de suas subclasses, se a validação falhar.

Observe que a validação real não precisa necessariamente acontecer no próprio membro público ou protegido. Ela pode acontecer em um nível inferior em alguma rotina privada ou interna. O ponto principal é que toda a área de superfície exposta aos usuários finais verifique os argumentos.

✔️ Gere ArgumentNullException se um argumento nulo for passado e o membro não oferecer suporte a argumentos nulos.

✔️ Valide parâmetros de enumeração.

Não suponha que os argumentos de enumeração estarão no intervalo definido pela enumeração. O CLR permite a conversão de qualquer valor inteiro em um valor de enumeração, ainda que o valor não esteja definido na enumeração.

❌ NÃO use Enum.IsDefined para verificações de intervalo de enumeração.

✔️ Lembre-se de que os argumentos mutáveis podem ter sido alterados após a validação.

Se o membro for sensível à segurança, você será incentivado a fazer uma cópia e, em seguida, validar e processar o argumento.

Passagem de parâmetro

Na perspectiva de um designer de estrutura, há três grupos principais de parâmetros: parâmetros por valor, parâmetros ref e parâmetros out.

Quando um argumento é passado por um parâmetro por valor, o membro recebe uma cópia do argumento real passado. Se o argumento for um tipo de valor, uma cópia do argumento será colocada na pilha. Se o argumento for um tipo de referência, uma cópia da referência será colocada na pilha. Por padrão, as linguagens CLR mais populares, como C#, VB.NET e C++, passam os parâmetros por valor.

Quando um argumento é passado por um parâmetro ref, o membro recebe uma referência do argumento real passado. Se o argumento for um tipo de valor, uma referência ao argumento será colocada na pilha. Se o argumento for um tipo de referência, uma cópia da referência será colocada na pilha. Os parâmetros Ref podem ser usados para permitir que o membro modifique os argumentos passados pelo chamador.

Os parâmetros Out são semelhantes aos parâmetros ref, com algumas pequenas diferenças. O parâmetro é inicialmente considerado não atribuído e não pode ser lido no corpo do membro antes de receber algum valor. Além disso, deve ser atribuído algum valor ao parâmetro antes que o membro retorne.

❌ Evite usar os parâmetros out ou ref.

O uso dos parâmetros out ou ref requer experiência com ponteiros, compreensão das diferenças entre tipos de valor e tipos de referência e os métodos de tratamento com vários valores de retorno. Além disso, a diferença entre parâmetros out e ref não é amplamente compreendida. Os arquitetos do Framework criando para um público geral não devem esperar que os usuários se tornem proficientes em trabalhar com parâmetros out ou ref.

❌ Não passe tipos de referência por referência.

Há algumas exceções limitadas à regra, como um método que pode ser usado para trocar referências.

Membros com número variável de parâmetros

Os membros que podem usar um número variável de argumentos são expressos fornecendo um parâmetro de matriz. Por exemplo, String fornece o seguinte método:

public class String {
    public static string Format(string format, object[] parameters);
}

Em seguida, um usuário pode chamar o método String.Format, da seguinte maneira:

String.Format("File {0} not found in {1}",new object[]{filename,directory});

A adição da palavra-chave params C# a um parâmetro de matriz altera o parâmetro para um parâmetro de matriz params e fornece um atalho para criar uma matriz temporária.

public class String {
    public static string Format(string format, params object[] parameters);
}

Isso permite que o usuário chame o método passando os elementos da matriz diretamente na lista de argumentos.

String.Format("File {0} not found in {1}",filename,directory);

Observe que a palavra-chave params só pode ser adicionada ao último parâmetro na lista de parâmetros.

✔️ Considere a adição da palavra-chave params aos parâmetros de matriz se você espera que os usuários finais passem matrizes com um pequeno número de elementos. Se é esperado que muitos elementos sejam passados em cenários comuns, os usuários provavelmente não passarão esses elementos embutidos de e, portanto, a palavra-chave params não é necessária.

❌ Evite usar matrizes de params se o chamador quase sempre tiver a entrada já em uma matriz.

Por exemplo, membros com parâmetros de matriz de bytes quase nunca seriam chamados passando bytes individuais. Por esse motivo, os parâmetros de matriz de bytes no .NET Framework não usam a palavra-chave params.

❌ NÃO use matrizes de params se a matriz for modificada pelo membro que usa o parâmetro de matriz params.

Como muitos compiladores transformam os argumentos para o membro em uma matriz temporária no site de chamada, a matriz pode ser um objeto temporário e, portanto, todas as modificações nela serão perdidas.

✔️ Considere o uso da palavra-chave params em uma sobrecarga simples, mesmo que uma sobrecarga mais complexa não possa usá-la.

Pergunte a si mesmo se os usuários valorizariam ter a matriz params em uma sobrecarga, mesmo que não estivesse em todas as sobrecargas.

✔️ Tente ordenar parâmetros para possibilitar o uso da palavra-chave params.

✔️ Considere fornecer sobrecargas especiais e caminhos de código para chamadas com um pequeno número de argumentos em APIs extremamente sensíveis ao desempenho.

Isso possibilita evitar a criação de objetos de matriz quando a API é chamada com um pequeno número de argumentos. Forme os nomes dos parâmetros tomando uma forma singular do parâmetro de matriz e adicionando um sufixo numérico.

Você só deve fazer isso se quiser diferenciar todo o caminho do código, não apenas criar uma matriz e chamar o método mais geral.

✔️ Lembre-se que nulo pode ser passado como um argumento de matriz params.

Você deve validar que a matriz não é nula antes do processamento.

❌ NÃO use os métodos varargs, também conhecidos como reticências.

Algumas linguagens CLR, como C++, dão suporte a uma convenção alternativa para passar listas de parâmetros variáveis chamadas métodos varargs. A convenção não deve ser usada em estruturas, pois não está em conformidade com CLS.

Parâmetros de Ponteiro

Em geral, os ponteiros não devem aparecer na área de superfície pública de uma estrutura de código gerenciado bem projetada. Na maioria das vezes, os ponteiros devem ser encapsulados. No entanto, em alguns casos, os ponteiros são necessários por motivos de interoperabilidade e o uso de ponteiros nesses casos é apropriado.

✔️ Forneça uma alternativa para qualquer membro que use um argumento de ponteiro, pois os ponteiros não são compatíveis com CLS.

❌ EVITE fazer uma verificação cara de argumentos de ponteiro.

✔️ Siga convenções comuns relacionadas ao ponteiro ao criar membros com ponteiros.

Por exemplo, não é necessário passar o índice inicial, pois a aritmética de ponteiro simples pode ser usada para obter o mesmo resultado.

Portions © 2005, 2009 Microsoft Corporation. Todos os direitos reservados.

Reimpresso com permissão da Pearson Education, Inc. das Diretrizes de Design do Framework: convenções, linguagens e padrões para bibliotecas do .NET reutilizável, 2ª edição por Krzysztof Cwalina e Brad Abrams, publicado em 22 de outubro de 2008 por Addison-Wesley Professional como parte da série de desenvolvimento do Microsoft Windows.

Confira também