Code Contracts, Null Checks, and Values ​​/ Reference Types

Updated post . To avoid confusion about what I am and what I am not doing, I edited this post radically to include a complete code example that causes this problem. In order for this message to be read in any case, all code is placed below.


Reference Information:

I am writing a free interface for testing (I know that it was done, but half the goal is to find out how it works ...), in which I want to check that myNumber is between 3 and 10 with a line of code, for example

 myNumber.ShouldBeLessThan(10).And.ShouldBeGreaterThan(10); myListOfCars.ShouldNotBeNull().And.ShouldBeA<IEnumerable<Car>>(); 

I think you can see, reading the second line, that it should check. Of course, there are more complex test cases ...

To enable the .And syntax, I introduced a helper type called AndHelper , which is returned by each validation method and has an And property that returns everything that was validated. Therefore .And in the previous example should return myNumber so that I can check another condition as well.

I use Code Contracts, and among other things, I verify that the this argument of some of these extensions is not null. This is causing my problem.


My problem:

When I run a Code check of my code, I get a bunch of warnings that a non-zero requirement, such as ShouldBeA<T> , cannot be checked. I tried to solve this by subclassing AndHelper<T> with two classes, ReferenceAndHelper<T> and StructAndHelper<T> , and ReferenceAndHelper<T> has contracts that should ensure that a non-empty requirement is met. However, this does not work.

Each time I use one of these test extensions, I get two warning messages. The statement that the contract "instance! = Null" cannot be verified, and the other - specify the location. The first points to the line where I use the method (for example, line 2 in the first example), and the second points to the line where the contract is indicated, marked // (1) in my code.


My code is:

Please bear with me that this part of the message is quite long. I don’t know what kind of SO recommendations for posting large code fragments (which is still relevant), but if there is a better way, please enlighten me.

Please note that there is code in this section that does not cause this particular error, but it introduces restrictions on the solution. For example, I should have a type ( AndHelper<T> or a subclass) that does not belong to the class / structure.

A couple of tests:

 // This test requires that instance != null, and therefore works // with ReferenceAndHelper<T> public static ReferenceAndHelper<T> ShouldBeA<T>(this object instance, string message = "") where T : class { Contract.Requires<ArgumentNullException>(instance != null); // (1) Contract.Ensures(Contract.Result<ReferenceAndHelper<T>>() != null); Assert.IsInstanceOf<T>(instance, message.AsNullIfWhitespace() ?? string.Format("ShouldBeA<{0}> failed.", typeof(T).Name)); return new ReferenceAndHelper<T>((T)instance); } // This test should work for both class and struct types T, and therefore // cannot decide between StructAndHelper<T> and ReferenceAndHelper<T>. // The base class is used. public static AndHelper<T> ShouldBeGreaterThan<T>(this T actual, T expected, string message = "") where T : IComparable { Contract.Ensures(Contract.Result<AndHelper<T>>() != null); (actual.CompareTo(expected) > 0).ShouldBeTrue(message.AsNullIfEmpty() ?? string.Format("ShouldBeGreaterThan failed. {0} is not greater than {1}", actual.ToString(), expected.ToString())); return new AndHelper<T>(actual); } // This is the test that returns the AndHelper<T> that .And is called on. // It is, as you can see, in all possible ways specified that this will be a // ReferenceAndHelper<T>, which has contracts to ensure that the value is not null. public static ReferenceAndHelper<T> ShouldNotBeNull<T>(this T value, string message = "") where T : class { Contract.Requires<ArgumentNullException>(value != null); Contract.Ensures(Contract.Result<ReferenceAndHelper<T>>() != null); Assert.IsNotNull(value, message.AsNullIfWhitespace() ?? "ShouldNotBeNull failed."); return new ReferenceAndHelper<T>(value); } 

AndHelper<T> class AndHelper<T> :

 public class AndHelper<T> { protected readonly T val; public AndHelper(T value) { this.val = value; } public virtual T And { get { return this.val; } } } 

Two subclasses of ReferenceAndHelper<T> :

 public class ReferenceAndHelper<T> : AndHelper<T> where T : class { public ReferenceAndHelper(T value) : base(value) { Contract.Requires(value != null); } public override T And { get { Contract.Ensures(Contract.Result<T>() != null); return val; } } [ContractInvariantMethod] void ValueIsNotNullInvariant() { Contract.Invariant(this.val != null); } } 

and StructAndHelper<T> :

 public class StructAndHelper<T> : AndHelper<T> where T : struct { public StructAndHelper(T value) : base(value) { } public override T And { get { return this.val; } } } 
+4
source share
3 answers

Instead of creating two AndHelper<T> classes with different constraints, could you just create a NonNullAndHelper<T> that states the invariant that its value is not zero? This will only be returned by helper functions that guarantee that their result is not zero, either due to a requirement or as a side effect of their function (e.g. IsNotNull). This should allow the contracts to prove.

+1
source

for code contracts does not exist check that AND (the on AndHelper property) will never return null

Why not? If I do not understand your question, you can encode something like this:

 public class AndHelper<T> { protected readonly T val; public T And { get { return val; } } public AndHelper(T value) { Contract.Requires(value != null); val = value; } [ContractInvariantMethod] void Invariants() { Contract.Invariant(And != null); } } 

From there, checking the contract ensures that the AND value will never be zero.

Do I really not understand your question?

+1
source

I know this is an old question, but I don’t see the accepted answers, so I thought that I would take a picture.

Instead of two subclasses of AndHelper<T> change your AndHelper<T> as follows:

 public class AndHelper<T> { private readonly T val; public AndHelper(T value) { Contract.Requires(!ReferenceEquals(value, null)); this.val = value; } public virtual T And { get { Contract.Ensures(!ReferenceEquals(Contract.Result<T>(), null)); return this.val; } } [ContractInvariantMethod] private void ObjectInvariant() { Contract.Invariant(!ReferenceEquals(val, null)); } } 

ReferenceEquals(object, object) does not raise a warning for generic types, but ensures that they are not null.

0
source

Source: https://habr.com/ru/post/1342870/


All Articles