Pergunta

O Visual Studio mostra um erro quando eu escrever este contrato abaixo.

Erro 20 Malformado do contrato seção no método '....get_Page'

É o problema com o 'se' bloco?

public int? Page
{
get
{
    int? result = Contract.Result<int?>();

    if (result != null)
        Contract.Ensures(result >= 0);

    return default(int?);
}
}

EDITAR:

Lasse V.Karisen postou nos comentários:

Como sobre:Contrato.Assegura(resultado == null || resultado >= 0);?

Sim Karisen, eu tentei isso antes e compila-lo.Mas a pergunta permanece:não é possível ter ifs quando usando contratos?

Outro problema que eu estou tendo é um ignorante (principalmente considerando o exemplo acima funciona), envolve o uso de resultado também:

public int IndexOf(T item)
{
    Contract.Assert(item != null);
    Contract.Assert((item as IEntity).ID > 0);

    int result = Contract.Result<int>();
    Contract.Ensures(result >= -1);

    return default(int);
}
Foi útil?

Solução

Apenas tendo um palpite. Talvez deva ser Contract.Ensures(result.Value >= 0)?

Outras dicas

O contrato é malformado porque todas as cláusulas de contrato devem aparecer perante qualquer outro código.

Você não precisa de um se, para fazer a manipulação booliana, o uso implica!

public int? Page
{
    get
    {
        Contract.Ensures( (result!= null).Implies(result >= 0) );
        var result = ...

        ...


        return result;
    }
}

Além disso, você deve usar requer não afirmar ao testar argumentos do método e outras pré -condições.

public int IndexOf(T item)
{
    Contract.Requires(item != null);
    Contract.Requires((item as IEntity).ID > 0);
...

Contrato tem sinalizador de compilação condicional neles. Em Liberação, mais seu código

if condition
    contract
return

torna-se

if condition
   return

Você vê o problema agora?

Todos os Garante e Exige chamadas deve ser antes de todas as outras declarações em um método ou uma propriedade do corpo, isso inclui atribuições simples como você está usando que ajudam a legibilidade.

Sintaxe adequada

public int? Page {
    get {
        Contract.Ensures(Contract.Result<int?>() == null 
            || Contract.Result<int?>() >= 0); 

        return default(int?);
        }
    }
 }

Este é muito feio, muito mais feia do que o normal if (x || y) throw new ArgumentOutOfRangeException().

Atributos Especiais

Há um pouco rotunda maneira de contornar isso. ContractAbbreviatorAttribute e ContractArgumentValidatorAttribute são os atributos especiais de que o ccrewrite entende que tornam a sua vida mais fácil.(Para lotes com mais detalhes, veja a System.Diagnostics.Contracts espaço de nomes de documentação no MSDN ou de Contratos de Código manual.)

Se utilizar .NET 4 ou mais: Esses atributos são, no âmbito de partida .NET 4.5, mas para versões anteriores, você pode obter um arquivo de origem para eles a partir do directório de Contratos de Código instala.(C:\Program Files (x86)\Microsoft\Contracts\Languages\) Em que pasta estão CSharp e VisualBasic subpastas que têm um ContractExtensions.cs (ou .vb) arquivo que contém o código necessário.

ContractAbbreviatorAttribute Este atributo efetivamente permite que você crie contrato de macros.Com isso, a sua página de propriedade poderia ser escrito assim:

public int? Page {
    get {
        EnsuresNullOrPositive();
        return default(int?)
    }
}

[ContractAbbreviator]
static void EnsuresNullOrPositive(int? x) {
    Contract.Ensures(
        Contract.Result<int?>() == null ||                 
        Contract.Result<int?>() >= 0);
}

EnsuresNullOrPositive poderia, também, ser mantidos em uma classe estática e reutilizados em seu projeto, ou divulgado e colocado em uma biblioteca de utilitários.Você também pode torná-lo mais geral, como o seguinte exemplo.

[ContractAbbreviator]
static void EnsuresNullOrPositive<Nullable<T>>(Nullable<T> obj) {
    Contract.Ensures(
        Contract.Result<Nullable<T>>() == null ||                 
        Contract.Result<Nullable<T>>() >= default(T));
}

Para minha própria biblioteca de utilitários, eu tenho uma classe estática chamada Requires e uma classe estática chamada Ensures, cada uma com vários métodos estáticos decorados com ContractAbbreviator.Aqui estão alguns exemplos:

public static class Requires {

    [ContractAbbreviator] 
    public static void NotNull(object obj) {  
        Contract.Requires<ArgumentNullException>(obj != null);
    }

    [ContractAbbreviator]
    public static void NotNullOrEmpty(string str) {
        Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(str));
    }

    [ContractAbbreviator]
    public static void NotNullOrEmpty(IEnumerable<T> sequence) {
        Contract.Requires<ArgumentNullException>(sequence != null);
        Contract.Requires<ArgumentNullException>(sequence.Any());
    }
}

public static class Ensures {
    [ContractAbbreviator]
    public static void NotNull(){
        Contract.Ensures(Contract.Result<object>() != null);
    }
}

Estes podem ser usados como este:

public List<SentMessage> EmailAllFriends(Person p) {
    Requires.NotNull(p); //check if object is null
    Requires.NotNullOrEmpty(p.EmailAddress); //check if string property is null or empty
    Requires.NotNullOrEmpty(p.Friends); //check if sequence property is null or empty
    Ensures.NotNull(); //result object will not be null

    //Do stuff
}

ContractArgumentValidatorAttribute Eu não usei essa fora de tutoriais, mas basicamente ele permite a você escrever pacote de vários if (test) throw new ArgumentException() chamadas em uma única chamada, que se comporta como uma chamada para Contract.Requires.Uma vez que lida apenas com o argumento de validação, ele não ajuda com o seu pós-condição de exemplo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top