Analisando a exceção ContractException


A partir de agora, durante a escrita do nosso código, podemos recorrer ao Code Contracts, que é uma biblioteca que está sendo desenvolvida pela Microsoft, e que permite trabalhar com condições e garantir com que elas sejam atendidas durante a execução ou até mesmo de forma estática. Caso alguma das regras impostas seja violada, uma exceção será disparada, caracterizando um bug no sistema que consome este componente. O uso desse novo recurso tende a diminuir consideravelmente o uso do código if-then-throw.

A exceção que é disparada é do tipo ContractException e, propositalmente, ela foi criada como internal. Isso evita que se crie tratadores de erro que capture este tipo de exceção, e com isso burle o processo de verificação que é realizado por essa biblioteca. A aplicação que consome a classe deve se preocupar apenas em capturar exceções tradicionais (ArgumentNullException, IndexOutOfRangeException, etc.), pois enquanto houver exceções do tipo ContractException sendo disparadas, provavelmente o consumidor não está passando as informações da forma correta.

Há alguns momentos em que gostaríamos de mudar este comportamento, como por exemplo, eliminar essa validação durante a escrita de algum teste (isso envolve outra discussão, que é TDD com DbC). Para exemplificar, considere o código abaixo, que possui uma condição pré (Requires) e outra pós (Ensures), que recorre ao Code Contracts para validar e garantir que as informações são passadas da forma correta para o método Creditar e também garante o estado da classe ContaBancaria, após a execução deste mesmo método.

public class ContaBancaria
{
    public decimal Saldo { get; private set; }

    public ContaBancaria(decimal saldo)
    {
        this.Saldo = saldo;
    }

    public void Creditar(decimal quantia)
    {
        Contract.Requires(quantia > 0, “A quantia para deposito deve ser maior que 0”);
        Contract.Ensures(Contract.OldValue(this.Saldo) + quantia == this.Saldo);

        this.Saldo += quantia;
    }
}

Se criarmos um código para consumir essa classe e, consequentemente, efetuar um crédito com valor negativo, teremos uma exceção do tipo ContractException. Para contornar esse comportamento, podemos recorrer ao evento estático ContractFailed da classe Contract. Esse evento define o argumento do tipo ContractFailedEventArgs, que expõe alguns membros interessantes, e alguns deles estão listados abaixo:

  • Condition: Uma string com a condição que não foi atentida.
  • FailureKind: Uma das opções exposta pelo enumerador ContractFailureKind, indicando se é uma condição pré, pós ou invariante.
  • Message: Uma string que representa a mensagem que descreve o problema.
  • OriginalException: Retorna a instância da exceção que causou o evento, que é utilizado quando você utiliza métodos que, por questões de compatibilidade, dispara exceções diferentes de ContractException.
  • SetHandled: Método que quando invoca, suprime o disparo da exceção que violou o contrato.
  • SetUnwind: Ao contrário do método anterior, quando invocado ele dispara a exceção ContractException depois que o tratador do evento ContractFailed é encerrado.

Com os membros que vimos acima, podemos agora ter acesso ao tipo do problema que ocorreu, e ainda conseguir burlar o problema de verificação que é realizado pelo Code Contracts. Abaixo temos um exemplo de como podemos proceder para fazer isso funcionar, ou seja, invocando o método SetHandled para que erros que violam o contrato não sejam mais entregues para o código consumidor:

static void Main(string[] args)
{
    Contract.ContractFailed += (sender, args) =>
    {
        Console.WriteLine(“{0} – {1}”, args.FailureKind, args.Condition);
        args.SetHandled();
    };

    new ContaBancaria(1000).Creditar(-100);
}

Publicidade

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s