Search code examples
c#code-contracts

Contracts - How to Require collection contains no nulls


I have an internal property on my class that returns List<MyType>, and I want to employ static checking to help me not do anything dumb in my assembly and possibly add a null to that collection.

I'm getting this static warning on the loop variable of a foreach over the property.

Warning 149 CodeContracts: Possibly calling a method on a null reference 'myLoopVariable'. Do you expect that System.Collections.Generic.List`1+Enumerator.get_Current returns non-null?

I've never had any problems in the past with my collections containing nulls, but when the compiler and me disagree, it's right and I'm wrong, therefore I want the warning gone. This is just something I never thought of before, and sure enough, I can do MyClass.MyListProperty.Add(null), so it is indeed valid.

I can not do Requires where I use the loop variable, because I get the error:

Contract section within a try block

It let me (without any error) add a Requires to the method:

Contract.Requires<ArgumentException>(Contract.ForAll<MyClass>(MyClassInstance.MyListProperty, x => x != null))

However, that did not cause the original warning to go away, and also resulted in an additional warning:

Warning 149 CodeContracts: requires unproven: Contract.ForAll(MyClassInstance.MyListProperty, x => x != null)

I'm wondering if there's a way I can put that logic in one place, close to the property definition itself.

What is the mechanism, if any, that code contracts provides to accomplish this?

EDIT:

I am beginning to think I'm going to have to make MyListProperty private again, and restrict access to it. Otherwise, someone (me) could add a null to it! (But the challenge there is, how to allow enumeration by a "trusted" visitor? oh my!)


Solution

  • The Assume will get of the message but I am not sure if this will lead to performance issues, also it seems redundant. Unfortunately I could not get rid of the static checker message without using Contract.Assume.

    // at the beginning of your method
    Contract.Requires(MyCollection != null && Contract.ForAll(MyCollection, x => x != null));
    // other code
    foreach(var item in MyCollection)
    {
       Contract.Assume(item != null);
       // call method on item which can create a message from the static checker if the assume is not included
    }