Pílula de
Atualização .NET - Code Contracts
Uma das novidades da plataforma .NET Framework 4.0 são os Code Contracts ou Contratos de Códigos.
O que vem a ser este tal de Code Contracts ????
Vamos começar com a definição formal:
"Os Contratos de Códigos permitem a você expressar pré-condições, pós-condições e invariantes de objeto em seu código para verificação em tempo de execução, análise estática e documentação."
Para podermos entender melhor temos que usar uma terminologia usada em contratos transpondo-a para área de desenvolvimento de software.
O que é uma pré-condição ? (Precondition)
É uma condição que deve ser satisfeita no início da execução de um código.(método, rotina, função,etc.)
O que é uma pós-condição ? (PostCondition)
É uma condição que deve ser garantida ao final da execução do código. (método, rotina, função,etc.)
O que é um Invariante ? (Invariant)
É uma condição que tem que ser verdadeira ao logo do ciclo de vida de um objeto.
Em resumo os Code Contracts da plataforma .NET seguem o princípio do Design by Contract que possuem três princípios que os contratos de código também devem cuidar:
1. Precondition : Refere-se a
coisas que ele espera fazer;
2. Postcondition : Refere-se a coisas
que ele garante fazer.
3. Invariant : Refere-se a coisas que
ele mantém;
![]() A programação por contrato(Design by Contract) é uma abordagem para projetar software de computador. Ela prevê que os criadores de software devem definir especificações de interface formal, precisas e verificáveis para componentes de software, que estendam a definição comum de tipos abstratos de dados com pré-condições, pós-condições e invariantes. Estas especificações são chamados de "contratos", de acordo com uma metáfora conceitual com as condições e obrigações dos contratos. |
Esse recurso tem o objetivo de permitir ao desenvolvedor uma forma fácil de definir um conjunto de condições ou regras para as classes como, por exemplo, obrigar o preenchimento de suas propriedades.
Além disso, como desenvolvedores temos a obrigação de validar qualquer entrada para um programa a partir de um agente externo. Para isso perdemos não pouco tempo desenvolvendo blocos de validações que verificam uma condição e lançam uma exceção com uma mensagem ao usuário.
Essa codificação pode acabar tornando o nosso código mais verboso e rebuscado tornando-o difícil de ler, entender e mais suscetível a falhas.
Apenas para ilustrar isso veja o código abaixo onde temos um método com 2 parâmetros que nos obrigou a escrever várias linhas de código para validação:
Public Sub
testeMacoratti(arg1 As String, arg2 As String) If arg1 Is Nothing Then Throw New ArgumentNullException("Argumento não pode ser nulo, Parameter Name: arg1") End If If arg2 Is Nothing Then Throw New ArgumentNullException("Argumento não pode ser nulo, Parameter Name: arg2") End If If arg1.Trim().Length = 0 OrElse arg2.Trim().Length = 0 Then Throw New ArgumentException("Argumento não pode ser vazio ou possuir espaços") End If If arg1.Equals(arg2) Then Throw New ArgumentOutOfRangeException("Os parâmetros não pode ser iguais, Parameter : arg1, arg2") End If 'lógica usada para validação Console.WriteLine("Os objetos não podem ser nulos, vazios nem iguais") End Sub |
Embora possamos mover a validação para outro lugar ainda sim cada método vai exigir a sua própria validação específica e sem perceber lá esta você escrevendo a sua livraria de códigos para validação.
O Code Contracts surge nesse cenário como uma ferramenta que você pode usar para facilitar o seu trabalho e tornar o seu código mais fácil de manter e de testar.
O papel do Code Contracts é escrever o código para você durante a execução nem mesmo tendo uma library padrão para lidar com ele mas também fazendo com que as instruções de validação estejam disponíveis antes de qualquer código.
Uma das grandes vantagens dos Code Contracts é que o seu assembly vai tratar cada uma dessas entradas de validação interna dentro do bloco if e também permitir que você escreva a verificação estática sobre estes durante a compilação.
Além disso através dos Code Contracts podemos :
Obs: Nas versões 2005 e 2008 você podia usar este recurso com o Spec#. Na versão 2010 os recursos estão disponíveis no namespace System.Diagnostics.Contracts
Usando Code Contracts na prática
Vamos abrir o Visual Studio 2010 (eu estou usando a versão Ultimate) e criar um novo projeto do tipo console usando a linguagem Visual Basic chamado CodeContracts;
![]() |
Antes de continuar você deve verificar se tem o Code Contracts habilitado para o seu projeto.
Faça assim:
1- Clique com o botão direito sobre o nome do projeto e selecione a opção
Properties;
2- Na janela de Propriedades localize a aba Code Contracts conforme
mostra a figura abaixo:
![]() |
Conseguiu localizar a aba Code Contracts ?
Se você não achou aba deverá instalar a ferramenta neste link => http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
Essa instalação é necessária para ter a verificação de tipo estática para o seu Visual Studio. Para isso, faça o download do instalador a partir do link DevLabs. Uma vez que você instale com sucesso, basta criar um novo projeto no seu Visual Studio e você poderá verificar a nova aba na seção de propriedades do projeto para configurar o código de seu contrato com MSBuild.
![]() |
Ao lado temos o botão para download da ferramenta: Sugiro que você selecione a versão : Premium Edition Se desejar aproveite para dar uma olhada na documentação. |
Após você fazer o download basta instalar (antes feche o projeto do Visual Studio se ele estiver aberto) para ter o Code Contracts ativo no seu Visual Studio.
Com tudo instalado vamos voltar ao nosso projeto...
Abra o projeto e na janela de propriedades, selecione a aba Code Contracts e marque as opções RunTime Checking e Static Checking conforme mostra a figura abaixo:
![]() |
Primeiro vamos criar um método usando o código normal idêntico ao usado no início do arquivo.
Abaixo vemos o método testeMacoratti() que recebe dois argumentos e abaixo vemos dois possíveis resultados. O primeiro lançando a exceção e o segundo quando a lógica usada é verificada com sucesso.
Module Module1 Sub Main() Dim nome As String = "Macoratti" Dim site As String = "Macoratti.net" Try testeMacoratti(nome, site) Catch ex As Exception Console.WriteLine(ex.Message) End Try Console.ReadKey() End Sub Public Sub testeMacoratti(arg1 As String, arg2 As String) If arg1 Is Nothing Then Throw New ArgumentNullException("Argumento não pode ser nulo, Parameter Name: arg1") End If If arg2 Is Nothing Then Throw New ArgumentNullException("Argumento não pode ser nulo, Parameter Name: arg2") End If If arg1.Trim().Length = 0 OrElse arg2.Trim().Length = 0 Then Throw New ArgumentException("Argumento não pode ser vazio ou possuir espaços") End If If arg1.Equals(arg2) Then Throw New ArgumentOutOfRangeException("Os parâmetros não pode ser iguais, Parameter : arg1, arg2") End If 'lógica usada para validação Console.WriteLine("Logica Usada ::> Os objetos não podem ser nulos, vazios nem iguais. OK") Console.ReadKey() End Sub End Module |
![]() |
Vejamos agora como usar o recurso do Code Contracts com base neste exemplo.
Testando uma PreCondition
Lembre-se que uma condição prévia ou pré-condição (PreCondition) significa que algo precisa ser satisfeito antes de ser executado, e portanto se uma PreCondition falha nenhuma código posterior será executado.
No menu Project clique em Add Class e informe o nome TesteContracts.vb, a seguir defina o código abaixo nesta classe:
Imports System.Diagnostics.Contracts Public Class TesteContracts Public Sub InvocarContrato(ByVal
arg1 As String, ByVal arg2 As String) End Class |
Observe que estamos usando o namespace : Imports System.Diagnostics.Contracts
No código se o arg1 e arg2 forem nulos ou forem iguais será lançada uma exceção com uma mensagem customizada.
Note que estamos fazendo uma chamada para o método estático Contract.Requires pois é através deste método que garantimos as pré-condições.
O método Contract.Requires permite que você passe uma condição e uma mensagem de forma seja lançada uma exceção sempre que a condição não for satisfeita. Esse método assegura que a exceção ocorra antes, sendo que ela é tratada como uma condição prévia.
Antes de continuar abra a janela de propriedades do projeto e selecione a aba Code Contracts;
Em Assembly Mode marque a opção Standard Contract Requeries conforme mostra a figura abaixo:
![]() |
Quando estamos testando um condição prévia (PreCondition) que lança uma exceção precisamos ativar a verificação em tempo de execução ; alterando o AssemblyMode para Standard , que reescreve as instruções IL durante a compilação, a exceção vai passar.
A seguir no Módulo defina o código abaixo onde iremos chamar o método InvocarContrato() da classe TesteContracts:
Module Module1 Sub Main() Dim cc As New TesteContracts Try cc.InvocarContrato(Nothing, Nothing) Console.ReadKey() Catch ex As Exception Console.WriteLine(ex.Message) Console.ReadKey() End Try End Sub End Module |
Executando o código acima teremos:
![]() |
Observe o resultado: Veja que o compilador nos alerta que um contrato foi violado, no caso uma pré-condição (Precondition failed).
Testando uma PosCondition
Uma pós condição (PostCondition) executa o seu código e lança uma
exceção depois de executá-lo com sucesso.
O método Contract.Ensures permite definir uma Pós-Condição. Vamos alterar
o código da nossa classe TesteContracts usando este método para testar
uma pós-condição conforme abaixo:
Imports System.Diagnostics.Contracts Public Class TesteContracts Public Sub InvocarContrato(ByVal
arg1 As String, ByVal arg2 As String)
'Contract.Requires(Of ArgumentException)(arg1.Trim().Length
> 0 AndAlso arg2.Trim().Length > 0, "Os argumentos não podem ser vazios")
'lógica usada para validação End Class |
Note que eu comentei as duas linhas usadas no exemplo anterior e as substitui por duas linhas onde utilizo o método Ensures.
Vamos agora alterar também o código do nosso Módulo usado para testar o Code Contracts conforme abaixo:
Module Module1 Sub Main() Dim cc As New TesteContracts Try Console.WriteLine("Informe o argumento 1" & vbNewLine) Dim arg1 As String = Console.ReadLine() Console.WriteLine("Informe o argumento 2" & vbNewLine) Dim arg2 As String = Console.ReadLine() cc.InvocarContrato(arg1, arg2) Console.ReadKey() Catch ex As Exception Console.WriteLine(ex.Message) Console.ReadKey() End Try End Sub End Module |
Ao definir a pós condição dissemos que após o método acabar de ser executado os argumentos não podem ser vazios nem iguais.
Executando o projeto e informando dois argumentos iguais iremos obter:
![]() |
Observe a mensagem gerada após a execução do método indicando que houve uma violação da pós condição (PostCondition failed).
E para encerrar Invariants
Bem, até onde entendemos, uma pré-condição e uma pós-condição só funcionam quando há uma chamada para o programa. As Invariantes trabalham de uma forma um pouco diferente.
Invariantes são as condições especiais que devem avaliar a verdade antes e
após a execução do código. Ou seja uma invariante é uma condição que deve ser
satisfeita ao longo de toda a vida de um objeto.
Por isso elas garantem que o objeto seguirá as condições, mesmo que existam
subtipos nos quais você sobrescreveu os métodos, para cada bloco do método como
propriedades, eventos, e métodos. Dessa forma elas garantem que nunca haverá uma
situação para todo o objeto, onde a condição falha.
Para usar invariantes temos que criar um método que infere as condições.
Vamos definir então um classe chamada Login.vb com o seguinte código:
Imports System.Diagnostics.Contracts Public Class Login Public Property Login() As String Get Return _login End Get Set(value As String) _login = value End Set End Property Private _login As String Public Property Senha() As String Get Return _senha End Get Set(value As String) _senha = value End Set End Property Private _senha As String Public Sub AlteraSenha(_novaSenha As String) Me.Senha = _novaSenha End Sub <ContractInvariantMethod()> _ Private Sub ObjectInvariant() Contract.Invariant(Me.Senha IsNot Nothing) End Sub End Class |
Aqui você deve observar que <ContractInvariantMethod> é um atributo especial que designa o método a ser tratado com condições Invariantes. Você não tem permissão para escrever qualquer coisa que não seja um sequência Contract.Invariant neste método e o método deve ser privado.
Assim marcamos o método com um atributo <ContractInvariantMethod> e neste método definimos o contrato chamando o método Invariant.
Neste exemplo definimos que um objeto do tipo Login não poderá ter uma senha nula.
Para testar vamos incluir o seguinte código no método Main() do módulo:
Sub Main() Console.WriteLine("Testando Invariants" & vbNewLine) Console.WriteLine("Vou alterar a senha para nothing para simular o erro" & vbNewLine) Try Dim login = New Login login.AlteraSenha(Nothing) Catch ex As Exception MsgBox(" Erro : " & ex.Message) End Try Console.ReadKey() End Sub |
Executando o projeto iremos obter :
![]() |
E assim vimos que com Code Contracts podemos separar o que são condicionais e o que são regras e também podemos gerar documentação com as informações do contrato; mas a possibilidade de encontrar chamadas inválidas antes que elas possam causar um erro usando a checagem estática faz de Code Contracts uma ferramenta a ser avaliada e estudada com atenção.
"Em verdade , em verdade vos digo que
vem a hora, e agora é, em que os mortos ouvirão a voz do Filho de Deus, e os que
a ouvirem viverão."(João-5:25)
Referências: