Monday 1 January 2018

The state of C# code contracts

If your following defensive programming principles, a good function should always check its inputs to protect itself from receiving bad data. If you are writing an API library that will be used by other upstream applications or via a publicly available resource, then you need to ensure that the parameters that are passed to its API don’t contain illegal arguments. If there is a bug in production then ideally I would like an exception to be raised and the error to be logged somewhere.

For example, the following two functions don’t contain any defensive measures:

class Program
{
    static void Main(string[] args)
    {
        CreateUser(null, null);
        SetAlarmTime(1234567);
    }

    static void CreateUser(string firstName, string lastName)
    {
        // will this function work when nulls are passed in?
    }

    static void SetAlarmTime(int hours)
    {
        // will this function work if hours is greater than 24?
    }
}

As a reader of the above code, you can’t tell what these two functions expect from their parameters. Is it okay to pass null arguments to CreateUser? Is the value 1234567 in the valid range for the function SetAlarmTime? There is no way to tell with the code above.

What about this revised version?

class Program
{
    static void Main(string[] args)
    {
        CreateUser(null, null);
        SetAlarmTime(1234567);
    }

    static void CreateUser(string firstName, string lastName)
    {
        // check inputs
        if (string.IsNullOrEmpty(firstName)) throw new ArgumentException("firstName cannot be null");
        if (string.IsNullOrEmpty(lastName)) throw new ArgumentException("lastName  cannot be null");
       
        // do work!
    }

    static void SetAlarmTime(int hours)
    {
        // check inputs
        if (hours < 1 || hours > 12) throw new ArgumentOutOfRangeException("hours must be between 1 and 12");

        // do work!
    }
}

That’s better and guarantees that the function will run only when the arguments that have been passed to it have satisfied the conditions of the function. What would be even better, is if that exception was logged somewhere for you to inspect later on.

Code Contracts

Now, this is where I would introduce Code Contracts. They let you do this and much more. There are lots of good blog posts on the subject. For example here, here, and here

With contracts, essentially the code above gets converted to the neater version below:

class Program
{
    static void Main(string[] args)
    {
        CreateUser(null, null);
        SetAlarmTime(123456);
    }

    static void CreateUser(string firstName, string lastName)
    {
        // check inputs
        Contract.Requires(!string.IsNullOrEmpty(firstName), "firstName cannot be null");
        Contract.Requires(!string.IsNullOrEmpty(lastName), "lastName cannot be null");
       
        // do work!
    }

    static void SetAlarmTime(int hours)
    {
        // check inputs
        Contract.Requires(hours > 1 && hours < 12, "hours must be between 1 and 12");
        // do work!
    }
}

In the event of an invalid argument, they still generate an exception and a contract exception is raised and thrown from the function.

But this is just the tip of the iceberg. Code Contracts also allow you to set post conditions to ensure return values also conform to a specified contract

So what’s the problem?

Here’s the problem. - Code Contracts never made it out of research and it was never included in a shipping version of Visual Studio!

You always had to install the extension from the visual studio marketplace. While the Contract.Requires APIs are available in the .NET framework they do nothing until you install the Code Contracts add-in. However, it seems that the project is pretty much dead now. The guy who championed the plugin has apparently left Microsoft and there is currently no support for them in .NET Core. Oh, and the biggie is that the Code Contracts extension doesn’t work in the latest version of Visual Studio 2017!

If you're stuck down this cul-de-sac and are migrating your code to .net core, someone has even written a contracts remover tool for converting pre-conditions back to "if-throw" syntax and removing all other contracts-related code.

Mads Torgersen – the Program Manager for the C# Programming Language at Microsoft left a comment here talking about the future of C# and code contracts and said:

"While people do use CodeContracts for many things, the 90% case seems to be dealing with null. We are eager to tackle the language integration of that in a more type-system-oriented way, allowing you to distinguish between nullable and nonnullable reference types and track correct usage based on that. I hope this can happen in the next major release of C#."

Mads is right, at least for me. I don't care to count the number of times I have been bitten by a null reference exception and the majority of times this is what I am guarding against. Tony Hoare, null's creator, regrets its invention and calls it his billion dollar mistake! (By the way, just so we are all on the same page, he's not talking about database nulls here)

In newer languages like Swift, Rust and Kotlin, they have built-in null safety from the start. For example, by default Kotlin doesn't allow nullable types and you have to explicitly say this type can hold null by adding the ? operator. It would be good to see something like this in the future for C#.

However, as the C# language is now open source there is this open issue on github to champion method contracts here and there is also an open discussion on the future of code contracts on its github pages here

Where does this leave us?

Apparently non-nullable reference types are coming to C# 8 but that has not been released yet! In fact there is no release date at the moment.

Until then, for null checks there is a github library named NullGuard that allows you to decorate a function with [AllowNull] or [NotNull]

public void SomeMethod([NotNull] string arg)
{
    // throws ArgumentNullException if arg is null.
}

public void AnotherMethod(string arg)
{
    // arg may be null here
}

For other more complex checks, I plan to continue throwing ArgumentExceptions and keeping an eye on the github pages for the future of code contracts


Contact Me:  ocean.airdrop@gmail.com

Popular Posts

Recent Posts

Unordered List

Text Widget

Pages