问题
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?
回答1:
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.
来源:https://stackoverflow.com/questions/11642857/codecontracts-reuse-of-assumptions-asserts