1

I've posted this on the CodeContracts forum at the MSDN but apparently no one knows or bothers to look into this issue.

I've tried to reduce the repetitive asserts and make it more reusable but unfortunately this doesn't work can you explain why?

[ContractVerification(false)]
public static class Assert
{
    [Conditional("DEBUG")]
    public static void GreaterThan<T>(T value, T lowerBound) where T : IComparable<T>
    {
        Contract.Ensures(value.CompareTo(lowerBound) > 0);
    }

    [Conditional("DEBUG")]
    public static void GreaterThanOrEqual<T>(T value, T lowerBound) where T : IComparable<T>
    {
        Contract.Ensures(value.CompareTo(lowerBound) >= 0);
    }

    [Conditional("DEBUG")]
    public static void LessThan<T>(T value, T upperBound) where T : IComparable<T>
    {
        Contract.Ensures(value.CompareTo(upperBound) < 0);
    }

    [Conditional("DEBUG")]
    public static void LessThanOrEqual<T>(T value, T upperBound) where T : IComparable<T>
    {
        Contract.Ensures(value.CompareTo(upperBound) <= 0);
    }

    [Conditional("DEBUG")]
    public static void NotNull(object value)
    {
        Contract.Ensures(value != null);
    }

    [Conditional("DEBUG")]
    public static void NotNullOrEmpty(string value)
    {
        Contract.Ensures(!string.IsNullOrEmpty(value));
    }

    [Conditional("DEBUG")]
    public static void True(bool value)
    {
        Contract.Ensures(value);
    }

    [Conditional("DEBUG")]
    public static void False(bool value)
    {
        Contract.Ensures(!value);
    }

    [Conditional("DEBUG")]
    public static void InRange<T>(T value, T lowerBound, T upperBound, ExclusionMode exclusionMode = ExclusionMode.None) where T : IComparable<T>
    {
        Contract.Ensures(((exclusionMode | ExclusionMode.LowerBound) == ExclusionMode.LowerBound ? value.CompareTo(lowerBound) > 0 : value.CompareTo(lowerBound) >= 0) && ((exclusionMode | ExclusionMode.UpperBound) == ExclusionMode.UpperBound ? value.CompareTo(upperBound) < 0 : value.CompareTo(upperBound) <= 0));
    }
}

I changed it to the following and it seems to work but obviously the generic version is more desirable.

[ContractVerification(false)]
public static class Assert
{
    [Conditional("DEBUG")]
    public static void GreaterThan(int value, int lowerBound)
    {
        Contract.Ensures(value > lowerBound);
    }

    [Conditional("DEBUG")]
    public static void GreaterThanOrEqual(int value, int lowerBound)
    {
        Contract.Ensures(value >= lowerBound);
    }

    [Conditional("DEBUG")]
    public static void LessThan(int value, int upperBound)
    {
        Contract.Ensures(value < upperBound);
    }

    [Conditional("DEBUG")]
    public static void LessThanOrEqual(int value, int upperBound)
    {
        Contract.Ensures(value <= upperBound);
    }

    [Conditional("DEBUG")]
    public static void NotNull(object value)
    {
        Contract.Ensures(value != null);
    }

    [Conditional("DEBUG")]
    public static void NotNullOrEmpty(string value)
    {
        Contract.Ensures(!string.IsNullOrEmpty(value));
    }

    [Conditional("DEBUG")]
    public static void True(bool value)
    {
        Contract.Ensures(value);
    }

    [Conditional("DEBUG")]
    public static void False(bool value)
    {
        Contract.Ensures(!value);
    }

    [Conditional("DEBUG")]
    public static void InRange(int value, int lowerBound, int upperBound, ExclusionMode exclusionMode = ExclusionMode.None)
    {
        Contract.Ensures(((exclusionMode | ExclusionMode.LowerBound) == ExclusionMode.LowerBound ? value > lowerBound : value >= lowerBound) && ((exclusionMode | ExclusionMode.UpperBound) == ExclusionMode.UpperBound ? value < upperBound : value <= upperBound));
    }
}

I just want an explanation not even a solution, does it have to do with CodeContracts not operating directly on the source code but on the IL?

Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157
iam3yal
  • 2,188
  • 4
  • 35
  • 44

1 Answers1

2

What you want is entirely possible, but not many people know it. First, go on your computer to C:\Program Files (x86)\Microsoft\Contracts\Languages\CSharp and include ContractExtensions.cs in your project. It contains some attributes that you need.

Then, apply the ContractAbbreviator attribute to your methods. You can remove the [Conditional("DEBUG")] and [ContractVerification(false)] attributes as you can set the contract behavior for Debug and Release in the Code Contracts property page of your project. Note that you must call your contract methods at the start of the method, where you'd otherwise write the contracts. You cannot put any other code in the methods.

public static class Assert
{
    [ContractAbbreviator]
    public static void GreaterThan<T>(T value, T lowerBound)
        where T : IComparable<T>
    {
        Contract.Ensures(value.CompareTo(lowerBound) > 0);
    }

    // ...
}

While this is an answer to your question, it might not solve your problem. The reason is that the static checker cannot see that when a.CompareTo(b) > 0, a > b holds. So, this example will not work with your generic version, but will work with your non-generic version:

static int PlusOne(int value)
{
    #region Contract
    Contract.Requires(value > 0);
    Assert.GreaterThan(value, 0);
    #endregion
    return value + 1;
}

Edit:

Apparently I completely misunderstood your intentions with the Assert class. You can indeed do what was recommended on this forum.

However, you cannot expect the static checker to understand that, for example, from x.CompareTo(y) > 0 follows that x > y. The reason? You can put literally anything in these methods. For example:

public int CompareTo(MyType t)
{
    // Implementation not consistent with '>'
    return this.field1 == t.field1 ? -1 : 1;
}

public static operator bool >(MyType left, MyType right)
{
    // Implementation not consistent with CompareTo()
    return left.CompareTo(right) <= 0;
}

You might not even have a CompareTo. So the static checker cannot see the similarities between them.

Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157
  • I already wrote that it's working with the non-generic version, I know that. I've been working with CodeContracts since it was released and I know that your example not going to work, the static checker gonna complain on these assertions because ContractAbbreviator only works with requires and was never design for ensures or anything else. In your example you're asserting after the contract which is pointless, the point of assertion is checking the output of another function right after it was called. – iam3yal Jul 30 '12 at 21:33
  • Your example works because the static checker already verified the input, there's no function in the middle to return an arbitrary value that needs asserting. I'm not asking for solution I'm asking for this person to tell me why the generic version doesn't work? why can't it see it? what's the limitation? – iam3yal Jul 30 '12 at 21:47
  • You are seeing this wrong. `ContractAbbreviator` can work with both _requires_ and _ensures_. Only with the `ContractAbbreviator` attribute are the contracts in `GreaterThan` inserted right where the method call is made. It is no assertion (but you called it `Assert`) and it is not _after_ the contract. It is part of the contract. `PlusOne` in my example has one _requires_ and one _ensures_ (via `GreaterThan`). In the end, `GreaterThan` will never be called, only the contracts are copied. And I told you why the checker cannot work with both `CompareTo` and comparison operators. – Daniel A.A. Pelsmaeker Jul 30 '12 at 23:25
  • Well, I double checked that and you're right ContractAbbreviator can work for ensures but I didn't just go and named it assert, these are actually used for assertions as if you were using Contract.Assume and the one who told me to do that is Francesco at the CodeContracts forum when I asked specifically how can I create reusable assumptions. http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/6c6f1a96-4163-49d8-b788-5b50dd79025b – iam3yal Jul 31 '12 at 02:34
  • When I'm using ContractAbbreviator it doesn't work as expected because the static checker complains when it shouldn't, it should work exactly as if I was using Contract.Assume and the above implementation does that pretty good. With all due respect you didn't tell me anything new, I want to know why it can't evaluate the expression and be smart about it? I know that it has a limitation and that it can't evaluate it but why? I do appreciate your time. – iam3yal Jul 31 '12 at 02:48