Este artigo foi traduzido por máquina.

Cutting Edge

Configurações de contratos de código no Visual Studio 2010

Dino Esposito

image: Dino EspositoNo mês passado, apresentei os contratos de software como que elas sejam implementadas no Microsoft.NET Framework 4. Conhecido como código de contratos, contratos de software permitem express condições formais que seu código deve atender para poder funcionar. Contratos de código fazem com que seu código lançar uma exceção se um método não recebe os dados conforme o esperado ou se ela produz dados após não esperadas posteriores. Para obter um visão geral das pré-condições e pós-condições, você pode querer verificar para fora meu artigo na edição do mês passado (msdn.microsoft.com/magazine/gg983479).

Contratos de código fazem parte da.NET Framework 4, mas também dependem algumas instalações no Visual Studio 2010, como, por exemplo, ferramentas de tempo de execução, integração com o MSBuild e uma página de propriedades na caixa de propriedades do projeto. É importante observar que apenas escrever anteriores e posteriores não é suficiente. Você também precisará ativar a verificação de recursos para cada projeto aproveitar os contratos de software em tempo de execução. Você pode fazer isso através da página de propriedade do projeto de contratos de código no Visual Studio 2010.

Neste artigo, falarei sobre a finalidade das várias opções, você pode verificar ou selecionar e analisar mais profundamente a ferramenta regravador e práticas recomendadas para a coisa mais comum é fazer com que os contratos de código: validação de argumento.

A página de propriedades de contratos de código

Devem contrato de código anteriores e posteriores ser impostas em todas as compilações ou baseia-se apenas em depuração? Na prática tudo se reduz a sua concepção de um contrato de software. É parte do esforço de design? Ou é apenas uma medida de depuração?

Não se for uma função de design, há nenhuma razão para retirar contratos nas compilações lançadas. Se for apenas uma técnica de depuração, você não deseja tê-lo ao redor quando você compilar no modo de versão.

No.NET Framework, contratos de código fazem parte da estrutura e não são baked em qualquer um dos idiomas. Como resultado, é mais fácil de configurá-los em uma base por compilação em seu projeto. A.NET Framework da contratos de software, portanto, deixa para decidir quando e onde for apropriado implementar os contratos.

Figura 1 mostra a página de propriedades no Visual Studio 2010, através do qual você configurar como deseja que contratos de software a trabalhar para um aplicativo. Observe que essas configurações se aplicam em uma base por projeto e, portanto, podem ser adaptadas conforme apropriado.

The Property Page for Code Contracts in Visual Studio 2010

Figura 1 a página de propriedades para código contratos no Visual Studio 2010

Você pode selecionar a configuração escolhida (Debug, Release e assim por diante) e aplicar configurações apenas para que a configuração. Dessa forma, você pode ativar contratos de código para depuração, mas não para liberação e, mais importante, é possível reverter a decisão a qualquer momento.

A verificação de tempo de execução

Ativar contratos de código, você deve fazer a opção de realizar a verificação do contrato de tempo de execução. Se você deixar esse estiver desmarcada, então as instruções do contrato que pode ser necessário em todo o código-fonte produzirá efeito (exceto para Contract.Assert e Contract.Assume em qualquer compilação onde o símbolo DEBUG é definido, mas o que é a classificação de um ponto irrelevante). A caixa de seleção controla se a ferramenta regravador é disparada no final de cada etapa de compilação. O regravador é uma ferramenta externa que subseqüentes de software de contratos e modifica o código MSIL, colocando as verificações de pré-condição, pós-condição e constante nos lugares certos.

Observe, contudo, que se você tiver uma pré-condição como esse, você vai obter um runtime falha de declaração se Pise o regravador:

Contract.Requires<TException>(condition)

Figura 2 mostra a caixa de mensagem que você conseguir.

The Code Requires Runtime Contract Checking

Figura 2 o código requer contrato de tempo de execução verificação

Para ver como funciona a verificação de tempo de execução em detalhes, considere o seguinte código:

public Int32 Sum(Int32 x, Int32 y) {
  // Check input values
  ValidateOperands(x, y);
  ValidateResult();

  // Perform the operation
  if (x == y)
    return 2 * x; 
  return x + y;
}

Os detalhes do contrato são armazenados nos métodos ValidateXxx usando ContractAbbreviators, conforme discutido na coluna do mês passado. Aqui está o código-fonte para os métodos de ValidateXxx:

[ContractAbbreviator]
private void ValidateOperands(Int32 x, Int32 y) {
  Contract.Requires(x >= 0 && y >= 0);
}

[ContractAbbreviator]
private void ValidateResult() {
  Contract.Ensures(Contract.Result<Int32>() >= 0);
}

Se você usar Contract.Requires em vez de Contract.Requires <TException> e, em seguida, salvar-se da falha de Figura 2 se você manter o gravador fora em uma das compilações. Caixa de mensagem Figura 2 é devido para a implementação interna de Contract.Requires, que se parece com isso:

[Conditional("CONTRACTS_FULL")]
public static void Requires(bool condition, string userMessage) {
  AssertMustUseRewriter(
    ContractFailureKind.Precondition, "Requires");
}

public static void Requires<TException>(bool condition) 
  where TException: Exception {
  AssertMustUseRewriter(
    ContractFailureKind.Precondition, "Requires<TException>");
}

O atributo Conditional indica a compiladores que uma chamada de método deve ser ignorada a menos que o símbolo CONTRACTS_FULL é definido. Este símbolo é definido somente quando você ativa a opção de realizar a verificação do contrato de tempo de execução. Porque Contract.Requires <TException> condicionalmente não está definido e não possui o atributo, a verificação de regravador é executada e resulta em uma declaração com falha se a verificação em tempo de execução de contrato será desativada.

Vamos mover para frente e considerar o efeito de usar o regravador no código anterior. Você pode verificar facilmente você mesmo o que estou dizendo, simplesmente usando pontos de interrupção e pressionar Ctrl + F11 para exibir o modo de exibição de desmontagem no Visual Studio 2010. Figura 3 mostra o conteúdo do modo de exibição de desmontagem quando você depurar o método Sum compilado sem habilitar verificação de contrato de tempo de execução. Como você pode ver, o código-fonte é o mesmo que você escreveu na classe.

Disassembly View Without Runtime Contract Checking

Figura 3 exibição de desmontagem sem contrato de tempo de execução verificação

Quando você ativar a verificação de tempo de execução, a ferramenta regravador passa sobre o retorno do compilador e edita o código MSIL. Se você percorrer o mesmo código com contratos de código habilitado, você verá algo como Figura 4.

Postconditions Checked Past the Return Statement

Figura 4 pós-condições verificada após a instrução Return

A diferença notável é que o ValidateResult é chamado imediatamente antes de sair do método de soma e a instrução de retorno. Você não tem que ser um guru de MSIL para ler o que está acontecendo no código da Figura 4. Operandos são validados antes do método inicia honrar a posição mais elevada de pré-condições. O código que contém posteriores é movido para o final do método e código MSIL para a instrução return final apenas enquadra-lo. Mais interessante é que a primeira instrução de retorno — aquele que implementa um atalho no método Sum — agora só vai para o endereço onde começa a ValidateResult:

...
retornar 2 * x;
00000054 eax do mov, dword ptr [ebp-8]
00000057 adicionar eax, eax
00000059 mov dword ptr [ebp-0Ch], eax
0000005c nop
0000005d jmp 0000006B
...
ValidateResult();
0000006b push dword ptr ds: [02C32098h]
...

Voltando ao Figura 1, observe a lista suspensa próxima a caixa de seleção Executar verificação de contrato de tempo de execução. Essa lista permite que você indique o número de contratos de software que você deseja ativar. Existem vários níveis: completo, pré e Post, pré-condições, ReleaseRequires e None.

Total significa que há suporte para todos os tipos de contratos de software e nenhum significa que nenhum deles são levados em conta. Anteriores e subseqüentes exclui invariáveis. Pré-condições também exclui garante instruções.

ReleaseRequires não suporta o método Contract.Requires e só permite que você especifique pré-condições usando requer <TException> ou o formato de If-Then-Throw herdado.

A página de propriedades do projeto permite habilitar ou desabilitar a verificação em uma base por projeto, mas e se você deseja desativar a verificação somente em algumas seções do código de tempo de execução em tempo de execução? Nesse caso apenas você pode desativar a verificação de tempo de execução programaticamente usando o atributo ContractRuntimeIgnored. No entanto, uma versão mais recente (1.4.40307.0) adicionado uma nova opção de ignorar quantificadores também permite que você não executar quaisquer contratos que contêm referências a Contract.ForAll ou Contract.Exists.

Você pode aplicar o atributo para membros que você usar em expressões de contrato. Se o membro é decorado com o atributo a instrução de contrato inteira em que ele apareça está sujeito à verificação de tempo de execução. O atributo não é reconhecido nos métodos de contrato, tais como Assert e Assume.

Modo de assembly

As propriedades de contratos de código também permitem que você configure uma configuração do modo de Assembly para os contratos. A configuração se refere ao modo como você pretende executar a validação de argumento. Há duas opções possíveis: requer o contrato padrão e o conjunto de referência do contrato. As configurações do modo de Assembly ajudam ferramentas como regravador para ajustar o código e dar avisos apropriados quando necessário. Digamos que você use o modo de Assembly para declarar o uso pretendido de contratos de código para validação de parâmetro. As configurações do modo de Assembly apresentam algumas regras simples, que você deve preencher, caso contrário, você obterá um erro de compilação.

Modo de assembly deve ser definido como padrão Contract requer se você usar métodos requer e requer <T> para validar os argumentos do método. Você deve usar a validação de parâmetro personalizado se você usar qualquer instrução If-Then-Throw como pré-condições. Se você não usar a validação de parâmetro personalizado, a instrução será tratada como uma requer <T>. A combinação de validação de parâmetro personalizado e o uso explícito de qualquer forma de requer instruções, em vez disso, lançará um erro do compilador.

Qual é a diferença entre usar requer e usando as instruções If-Then-Throw? Uma instrução If-Then-Throw sempre lança a exceção que você indique se a validação falhar. Em relação a isso, ele difere do requer, mas é semelhante ao requer <T>. Uma simples instrução de If-Then-Throw também não é detectável por ferramentas de contrato (regravador e verificador estático), a menos que você o execute com uma chamada para EndContractBlock. Quando você usa EndContractBlock, ele deve ser o último método de contrato de código que você chamar o método. Nenhuma chamada de contratos de código nunca execute ele:

if (y == 0)
  throw new ArgumentException();
Contract.EndContractBlock();

Além disso, requer declarações são herdadas automaticamente. Uma instrução If-Then-Throw não é herdada, a menos que você também usar o EndContractBlock. No modo herdado, If-Then-Throw contratos não são herdados. Em vez disso, você deve fazer manualmente a herança de contrato. As ferramentas tentará Avisar se não detectar que as pré-condições são repetidas em substituições e implementações de interface.

Finalmente, observe que o ContractAbbreviators não podem conter quaisquer instruções If-Then-Throw, mas você pode usar os validadores de contrato para que. Abbreviators só pode incluir instruções de contrato regulares para validação de argumento.

Outras Configurações

Na página de propriedades de contratos de código, se você selecionar a opção de realizar a verificação do contrato de Runtime, irá ativar algumas opções úteis adicionais.

Se você ativar o Assert na opção de falha do contrato, qualquer violação de contrato resultará em uma declaração que descreve o contexto da falha. Você verá uma caixa de mensagem semelhante ao que é mostrado na Figura 2 e serão dadas algumas opções à sua escolha. Você pode, por exemplo, tentar anexar um depurador novamente, anular o aplicativo ou simplesmente ignorar a falha e prosseguir.

Convém usar esta opção para compilações de depuração só porque as informações exibidas não não provável que sejam significativos para o usuário final comum. A API de contratos de código oferece um manipulador de exceções centralizado que captura qualquer violação, deixando-o você descobrir o que deu errado exatamente. As informações que você recebe distingue uma pré-condição, uma pós-condição ou invariável falhou, mas só usa a expressão booleana e, possivelmente, a mensagem de erro configurado para caracterizar o erro. Em outras palavras, é um pouco difícil para você recuperar normalmente do manipulador de exceções centralizado:

Contract.ContractFailed += CentralizedErrorHandler;

Eis alguns códigos que ilustra o manipulador:

static void CentralizedErrorHandler(
  Object sender, ContractFailedEventArgs e) {
  Console.WriteLine("{0}: {1}; {2}", e.
FailureKind, e.Condition, e.Message);
  e.SetHandled();
}

Se você deseja lançar exceções específicas em tempo de execução, em seguida, usando requer <TException> é a melhor opção. Você pode querer usar requer e o manipulador centralizado se você pretende limitar o uso de contratos depurar compilações ou se você não se preocupa com o que é o tipo real da exceção. É com freqüência suficiente para indicar que ocorreu um erro. Por exemplo, no nível superior, muitos aplicativos têm uma pega-tudo que captura todos os tipos de exceção e determinam como a reinicialização.

Refere-se a opção somente contrato público de superfície para onde você gostaria de ter contratos de código imposta: cada método ou apenas métodos públicos. Se você marcar a opção, o regravador ignora as declarações de contrato de código nos membros particulares e protegidos, e só processa contratos em membros públicos.

Marcando esta opção faz sentido se incorporar código contratos como parte do seu design geral e, conseqüentemente, usá-los em todos os lugares. No entanto, depois que o aplicativo está pronto para ser entregue, como uma forma de otimização você pode desativar a carga extra de verificação de parâmetros de membros internos porque nenhum código externo será nunca ser invocar esses membros diretamente.

Se a limitação de contratos de código para a superfície de pública de um assembly é uma boa idéia também depende como você escreveu o código. É uma forma de otimização se pode garantir que todas as chamadas que faz com que a superfície de pública para níveis inferiores estão corretas. Caso contrário, a desativação de contratos para métodos internos pode se transformar em origem de bugs irritantes.

A opção de site de chamada requer verificação serve outro cenário de otimização. Suponha que você estiver escrevendo uma biblioteca a ser usada pelos módulos em outros assemblies. Por motivos de desempenho, você pode desativar a verificação de contratos de tempo de execução. No entanto, você também pode criar um assembly de referência do contrato, desde que permitem o chamador verificar se o contrato para cada método sendo chamado.

Um assembly de referência do contrato pode existir para cada assembly que contém classes usando código contratos. Ele contém a interface visível publicamente o assembly pai com as instruções do contrato, mas nenhum outro código. A criação do assembly pode ser solicitada e controlada a partir da página de propriedade de contratos de código.

Qualquer código que pretende chamar sua biblioteca pode fazer referência a seu conjunto de referência do contrato e poderia importar automaticamente os seus contratos, simplesmente ativando a opção de site de chamada requer verificação. Ao processar o código chamador, o regravador importará o contrato para cada método sendo chamado em um assembly externo que vem com um conjunto de referência do contrato. Nesse caso, o contrato é verificado no local de chamada — no lado do chamador — e permanece um comportamento opcional que pode ser ativado e desativado para código não controlar diretamente.

Conclusão

Contratos de código é uma área da.NET Framework que vale muito mais investigação. Eu já apenas superficialmente as opções de configuração aqui e ainda não tiver entrado no uso do verificador estático. Contratos de código ajudam a criar aplicativos com mais clareza e escrever código mais limpo.

Para saber mais sobre contratos de código, consulte a Junho de 2009 CLR coluna por Melitta Andersen (msdn.microsoft.com/magazine/ee236408) e o site de contratos de código DevLabs (msdn.microsoft.com/devlabs/dd491992). Você também encontrará informação interessante sobre o desenvolvimento de contratos de código no site da Microsoft Research código contratos (research.microsoft.com/projects/contracts).

Dino Esposito é o autor de "Programming Microsoft ASP.NET MVC"(Microsoft Press, 2010) e co-autor do"Microsoft.NET: arquitetura de aplicativos corporativos "(Microsoft Press, 2008). Residente na Itália, Esposito é um palestrante sempre presente em eventos do setor no mundo inteiro. Segui-lo no Twitter em twitter.com/despos.

Graças aos seguinte perito técnico para revisão deste artigo: Mike Barnett