4

I often find myself wondering what programming best practices apply to solo programming, since most of the time, I'm the only programmer on a project. I just started experimenting with C# 4.0 Code Contracts, and I thought I'd ask the community what utility they may have for a solo programmer.

Here's where I'm coming from with this. Most of the time, when I encounter an exception, it means there's a bug in my code. There are definite exceptions (no pun intended) to this, such as when I'm dealing with something external, such as a text file, database, or service, but for the most part, exceptions mean I did something wrong.

Take this simple method:

public void MapEntity(PersonModel model, PersonEntity entity)
{
    entity.Name = model.Name;
    entity.Age = model.Age;
    entity.Updated = DateTime.Now;
}

If either model or entity are null, this method will throw a NullReferenceException.

As the only consumer of my code, I know that I shouldn't pass in a null reference, but if I somehow do, I want an exception to be thrown, and I want the debugger to jump right to the source of the error so I can see what happened.

That said, I could write:

public void MapEntity(PersonModel model, PersonEntity entity)
{
    Contract.Requires(model != null);
    Contract.Requires(entity != null);
    entity.Name = model.Name;
    entity.Age = model.Age;
    entity.Updated = DateTime.Now;
}

This has the advantage of explicitly indicating (to myself) that the two arguments are not allowed to be null. However, it's very unlikely I would make this mistake (because I know what the method does). Further, if I understand properly what Requires() does, the behavior at runtime if a null argument is encountered would be pretty much the same. So, I can't help but wonder, what have I gained besides two lines of code?

As a counterpoint, here's a case where I see definite value in being explicit:

public User FindUser(string emailAddress)
{
    if (string.IsNullOrWhiteSpace(emailAddress)) return null;
    var users = DbContext.Users.Where(u => u.EmailAddress == emailAddress);
    Contract.Assert(users.Count() <= 1,
                    "The database appears to be corrupted:
                    More than one user was found for the provided email address.
                    Email addresses are supposed to be unique.");
    return users.SingleOrDefault();
}

But in this case, the reason I see value in using a coding contract is to indicate that something that has gone wrong that is not a bug per se (though it could have resulted from a bug elsewhere in the system).

I'd like to know if I'm on the right track with my thinking, or if there is more value in code contracts than I realize. Does it pay to use contracts when you can examine and debug the code yourself or only when the code is going to be used by a third party?

2 Answers 2

11

The advantage of assertions (or contracts, which are basically a different take on assertions) comes when your codebase grows large enough that you can no longer hold the entire thing in your short-term memory. Especially when you're still developing it and changing things every day.

When you first wrote MapEntity, you most likely wrote it because some other routine somewhere required its functionality. You were working on the other routine at the time, and you know it's not going to pass any nulls. But a few months from now, you might be writing something else that needs the same functionality, so you have it call MapEntity too. But maybe this code can have a null somehow and you didn't think to check for that.

With no assertions, you get a null reference exception at this point. With assertions in place, you get an assertion failure (or whatever violated contracts throw, in this case.) Either way, you have an error here and you have to dig into the code to figure out what's going on. You look at the code, and you see that the problem is in MapEntity because it's choking on one of the parameters. So is there a bug in MapEntity, or is routine that called it feeding it an invalid parameter?

One of the advantages of an assertion/contract is that it clearly documents the fact that you anticipated this problem and explicitly did not want it to happen. This makes figuring out how to fix it that much easier because it makes it easier to determine the source of the problem. In this case, it shows clearly that the problem is in the caller, and not in MapEntity.

On the other hand, sometimes I'll get an assertion failure in code that's receiving perfectly cromulent parameters, because the requirements have changed. This helps me see what has to be changed, and sometimes it's not as simple as "argument must not be null". Sometimes the argument has to have certain properties set certain ways or data would have been silently corrupted. Having an assertion in place makes it easier to remember what I was thinking when I wrote it, so I can fix the code more easily.

0
5

Mason's answer is excellent, but one other thing to remember is that contracts are a bit more than just a different take on assertions, if you're using the static analysis tool that is included in the Premium Edition of Microsoft's Code Contracts.

If your MapEntity() function starts off by requiring that both its arguments are non-null, then yes, you'll get an assertion if it is called with a null argument.

But assume you have code like this:

var model = new PersonModel();
var entity = GetPersonEntityFromDatabaseOrSomething(personId);
MapEntity(model, entity);

The static analysis tool will see that model is surely not null. But that entity might be, depending on what GetPersonEntityFromDatabaseOrSomething() returns. It will warn you about that.

At this point, you should add a postcondition to GetPersonEntityFromDatabaseOrSomething()

Contract.Ensures(entity != null);

That will satisfy the static analysis tool, since it can now be confident that the entity being passed to MapEntity() is not null. And if, due to a bug in GetPersonEntityFromDatabaseOrSomething(), you try to return a null value (e.g. id not found in database, and you don't handle it properly), you will get your assertion even earlier on - not at the point where you try to reference the null, but closer to the actual problem, where you try to return null from a function that is being relied upon to return a non-null object.

2
  • Carson, thanks and +1. That is exactly the kind of thing I was looking for. If I'm not mistaken, isn't there a Code Analysis feature built into Visual Studio 2010. Is this what you're referring to?
    – devuxer
    Jul 12, 2011 at 0:38
  • 1
    @DanM: The one I was thinking of isn't included in Visual Studio, I don't think? It's at this DevLabs: Code Contracts page. There are two versions, a standard one which does not include static analysis of your code, and a premium one which does - but requires VS2008 Team System or VS2010 Premium or Ultimate. Jul 12, 2011 at 1:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.