Elegant Invariant Checking with C# 3

Posted by Andrew on July 30, 2008

Of all the great advice in The Pragmatic Programmer, one of the ideas I’ve found most useful is that of Design by Contract. Most commonly, I check method preconditions using something like:

private void MyMethod(object arg1, string arg2)
{
  Invariant.ArgumentNotNull(arg1, "arg1"); // throws ArgumentNullException
  Invariant.ArgumentNotEmpty(arg2, "arg2"); // throws ArgumentOutOfRangeException
 
  // do stuff
}

I quite often check invariants at other points in a method too:

private void MyMethod(object arg1, string arg2)
{
  var myVar = AnotherMethod();
 
  Invariant.IsNotNull(myVar); // throws InvalidOperationException
 
  // do stuff with myVar
}

This approach works well but, in the case of preconditions, having to duplicate the argument name as a string is obviously not ideal. Perhaps using some of the new features of C# 3 we can do better?

Here’s one API I’ve been playing with:

private void MyMethod(object arg1, string arg2)
{
  Check.Argument(() => arg1.IsNotNull); // throws ArgumentNullException
  Check.Argument(() => arg2.IsNotEmpty); // throws ArgumentOutOfRangeException
 
  // do stuff
}

And for simple invariants:

private void MyMethod(object arg1, string arg2)
{
  var myVar = AnotherMethod();
 
  Check.Invariant(() => myVar.IsNotNull); // throws InvalidOperationException
 
  // do stuff with myVar
}

This approach leverages expression trees and extension methods to create a more fluent API and allows us to do away with the string argument when checking arguments.

Although I’m not 100% happy with it, I think I prefer this API. What do you think?

Download the code here.

kick it on DotNetKicks.com

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. DotNetKicks.com Wed, 30 Jul 2008 03:16:17 CDT

    Elegant Invariant Checking with C# 3 …

    You’ve been kicked (a good thing) - Trackback from DotNetKicks.com…

  2. James Newton-KingNo Gravatar Wed, 30 Jul 2008 03:42:12 CDT

    One problem I see is that it will pollute intellisense with IsNotNull, IsNotEmpty, etc methods.

  3. AndrewNo Gravatar Wed, 30 Jul 2008 04:42:41 CDT

    Perhaps, although one advantage is that these methods can actually be useful on their own. i.e:

    if (myString.IsNotEmpty())
    {
    // instead of !string.IsNullOrEmpty()
    }

    Even IsNull() is similar to the Ruby idiom of nil?

    Attributes can also be used to hide the methods from intellisense.

  4. Johnny HallNo Gravatar Wed, 30 Jul 2008 07:35:48 CDT

    I’m liking the look of this, although I haven’t been so concerned with the issue to evaluate it properly yet.

    http://research.microsoft.com/SpecSharp/

  5. SergeyNo Gravatar Thu, 31 Jul 2008 00:28:11 CDT

    Cool!

    Tought in boo it is much more simple :)

    def DbC[[Required(extendee != null, "extendee must be not null")]extendee as object]:

    PS
    What about ensures?

  6. John RuskNo Gravatar Thu, 31 Jul 2008 04:55:36 CDT

    What about putting the magic method later in the fluent API, outside the expression, something like:

    Check.That(()=> arg1).IsNotNull();

    In that case there’d be no extension methods, but would be too many parentheses for my liking…

    What about

    Require.Value( () => arg1);

  7. Poul FogedNo Gravatar Thu, 31 Jul 2008 07:23:48 CDT

    Whats wrong with:

    Invariant.ArgumentNotNull(arg1);

    or

    Invariant.Argument(arg1).IsNotNull();

    - reflection should help us reveal the parameters real name….

  8. Dan GoldsteinNo Gravatar Thu, 31 Jul 2008 09:07:33 CDT

    I’m not so sure I see a big improvement. Invariant.ArgumentIsNotNull has the advantage of having the requirement at the beginning, not polluting intellisense and not having an ugly parameterless lambda function. Your new method only saves the need to specify the name of the argument in addition to its value.

  9. RaimonNo Gravatar Thu, 31 Jul 2008 10:19:26 CDT

    this code can be used in generating documentation.

    Invariant.ArgumentNotNull(arg1, “arg1″); // throws ArgumentNullException
    Invariant.ArgumentNotEmpty(arg2, “arg2″); // throws ArgumentOutOfRangeException

    it’s cool

  10. Jason BockNo Gravatar Thu, 31 Jul 2008 11:04:58 CDT
  11. Judah HimangoNo Gravatar Thu, 31 Jul 2008 11:53:07 CDT

    Regarding your use of C# 3 syntax to get around the redundancy, I suggested something similar to the CuttingEdge.Conditions library:

    http://www.codeproject.com/script/Forums/View.aspx?fid=1498290&msg=2640907

    But the author said:

    I’m sorry Judah,

    but it’s even worse than you would expect. I just did some performance testing. I compared the following two lines:

    a.Requires(”a”).IsNotNull();

    compared to:

    a.Requires(() => a).IsNotNull();

    What’s the difference? Well, the first line actually doesn’t even allocate a string. It just uses a reference (a 32 bit number) to reference a string that is already allocated (and as long as that string isn’t used, there only the cost of copying an Int32 on the stack).

    The second isn’t a simple delegate allocation. It actually allocates 7 new objects on the heap, makes at least 9 (non-inlinable) method calls and does some heavy reflection to build up the Expression (see for yourself with Reflector). And the worst part is that it doesn’t do this only once during initialization. No, it does this every time a call to Requires(() => a) is made.

    I found that the second statement is actually over 200 times slower than the first. here
    you can take a look at my test code.

  12. Stuart ClarkNo Gravatar Thu, 31 Jul 2008 21:20:00 CDT

    I like the idea its got me thinking. The question I have is:

    Check.Argument(() => arg1.IsNotNull); // throws ArgumentNullException

    How would you determine the argument name when constructing your ArgumentNullException.

    Without that its the equivalent of your original code minus the string argument name.

    Invariant.ArgumentNotNull(arg1);

  13. AndrewNo Gravatar Thu, 31 Jul 2008 22:01:02 CDT

    Stuart,

    This is done by picking apart and inspecting the expression tree resulting from the lambda.

    Cheers,

    Andrew.

Comments