I have a collection of child items in my class and I have a public accessor to it. I want to provide a postcondition to ensure that items in the collection are not null (I know, that in tests 2 and 3 caller can change the collection, but for now my goal is just to make sure, that collection returned from the property doesn't contain null items).
I thought using Assume and ForAll would be enough, but that doesn't help
Here is the sample code with 3 classes that I tried. All 3 cases are allmost identical except that first exposes ReadOnlyObservableCollection, 2nd - ObservableCollection, and 3rd - List.
- ReadOnlyObservableCollection
class Test1
{
public Test1()
{
_children = new ObservableCollection<A>();
_childrenReadOnly = new ReadOnlyObservableCollection<A>(_children);
}
protected readonly ObservableCollection<A> _children;
protected readonly ReadOnlyObservableCollection<A> _childrenReadOnly;
public ReadOnlyObservableCollection<A> Children
{
get
{
Contract.Ensures(Contract.ForAll(Contract.Result<ReadOnlyObservableCollection<A>>(), i => i != null));
Contract.Assume(Contract.ForAll(_childrenReadOnly, i => i != null));
return _childrenReadOnly; // CodeContracts: ensures unproven: Contract.ForAll(Contract.Result<ReadOnlyObservableCollection<A>>(), i => i != null)
}
}
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(_children != null);
Contract.Invariant(_childrenReadOnly != null);
}
}
- ObservableCollection
class Test2
{
public Test2()
{
_children = new ObservableCollection<A>();
}
protected readonly ObservableCollection<A> _children;
public ObservableCollection<A> Children
{
get
{
Contract.Ensures(Contract.ForAll(Contract.Result<ObservableCollection<A>>(), i => i != null));
Contract.Assume(Contract.ForAll(_children, i => i != null));
return _children; // CodeContracts: ensures unproven: Contract.ForAll(Contract.Result<ObservableCollection<A>>(), i => i != null)
}
}
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(_children != null);
}
}
- List
class Test3
{
protected readonly List<A> _children = new List<A>();
public List<A> Children
{
get
{
Contract.Ensures(Contract.ForAll(Contract.Result<List<A>>(), i => i != null));
Contract.Assume(Contract.ForAll(_children, i => i != null));
return _children; // No warning here when using List instead of ObservableCollection
}
}
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(_children != null);
}
}
Here is the test code that uses this classes:
Test1 t1 = new Test1();
foreach (A child in t1.Children)
{
child.SomeMethod(); // CodeContracts: Possibly calling a method on a null reference 'child'
}
Test2 t2 = new Test2();
foreach (A child in t2.Children)
{
child.SomeMethod(); // CodeContracts: Possibly calling a method on a null reference 'child'
}
Test3 t3 = new Test3();
foreach (A child in t3.Children)
{
child.SomeMethod(); // CodeContracts: Possibly calling a method on a null reference 'child'
}
Can I somehow define a contract, in order not to write Contract.Assume(child != null)
every time I use Children
property?
I tried to implement Enumerator
that ensures not null condition in Current
property getter, as was suggested by phoog. But the warning is still present (surprisingly for me).
public class NotNullEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> _enumerable;
public NotNullEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return new NotNullEnumerator<T>(_enumerable.GetEnumerator());
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
public class NotNullEnumerator<T> : IEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
public NotNullEnumerator(IEnumerator<T> enumerator)
{
_enumerator = enumerator;
}
#region IEnumerator<T> Members
public T Current
{
get
{
Contract.Ensures(Contract.Result<T>() != null);
return _enumerator.Current;
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
_enumerator.Dispose();
}
#endregion
#region IEnumerator Members
object System.Collections.IEnumerator.Current
{
get
{
Contract.Ensures(Contract.Result<object>() != null);
return _enumerator.Current;
}
}
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public void Reset()
{
_enumerator.Reset();
}
#endregion
}
Usage in code:
Test1 t1 = new Test1();
var NonNullTest1 = new NotNullEnumerable<A>(t1.Children);
foreach (A child in NonNullTest1)
{
child.SomeMethod(); // CodeContracts: Possibly calling a method on a null reference 'child'
}
Any ideas?
I would create my own collection type. For example, you could implement IList<T>
and "ensure" that the index getter never returns null, and "require" that Add()
and the index setter never receive null as an argument.
EDIT:
To avoid the "possibly calling a method on a null reference" message in the foreach loop, you would probably also have to implement your own enumerator type and "ensure" that its Current
property never returns null.
EDIT2:
Since ObservableCollection<>
and ReadOnlyObservableCollection<>
both decorate an IList<>
instance and therefore implement IList<>
, I tried the following. Note the inconsistency between the "ensures unproven" and the "assert is false". I got the same messages whether the static type of result
was ReadOnlyObservableCollection<C>
or IList<C>
. I'm using Code Contracts version 1.4.40602.0.
namespace EnumerableContract
{
public class C
{
public int Length { get; private set; }
}
public class P
{
public IList<C> Children
{
get
{
Contract.Ensures(Contract.Result<IList<C>>() != null);
Contract.Ensures(Contract.ForAll(Contract.Result<IList<C>>(), c => c != null));
var result = new ReadOnlyObservableCollection<C>(new ObservableCollection<C>(new[] { new C() }));
Contract.Assume(Contract.ForAll(result, c => c != null));
return result; //CodeContracts: ensures unproven Contract.ForAll(Contract.Result<IList<C>>(), c => c != null)
}
}
public class Program
{
public static int Main(string[] args)
{
foreach (var item in new P().Children)
{
Contract.Assert(item == null); //CodeContracts: assert is false
Console.WriteLine(item.Length);
}
return 0;
}
}
}
}
EDIT3:
Found a good summary of the issues at http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/af403bbc-ca4e-4546-8b7a-3fb3dba4bb4a/; basically, adding additional conditions to the contract of an implemented interface violates the Liskov substitution principle, because it means that the class with additional restrictions cannot be used in any context that accepts an object implementing that interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With