2011-01-21

New Library: ConnectedProperties

The first version of ConnectedProperties has been released (on both CodePlex and NuGet).

It's a very simple library that allows you to dynamically attach properties to (most) objects at runtime. It uses the ConditionalWeakTable I posted about last month, but adds some wrapping to ensure it's used correctly.

Download it, check it out, and leave some feedback (on CodePlex or the NuGet gallery, as appropriate)!

2011-01-19

Simple and Easy Code Contracts

Code Contracts are a wonderful thing. Every new library I write uses CC right from the beginning.

Here's Steve's "simple and easy" guide to getting started with Code Contracts (assuming you're writing a new library):

Project Setup

After you've downloaded and installed Code Contracts and created a library project, you need to enable CC for that library. Under the Project Properties, there is a new tab called Code Contracts. Here's the way that I like to set it up:

  • Set "Assembly Mode" to "Standard Contract Requires".
  • Debug - Check "Perform Runtime Contract Checking" and set to "Full". Check "Call-site Requires Checking".
  • Release - Set "Contract Reference Assembly" to "Build" and check "Emit contracts into XML doc file".

In addition, if you have the Academic or Commercial Premium edition of Code Contracts, add a configuration called CodeAnalysis with all the settings from Debug and also check "Perform Static Contract Checking" and all the other checkboxes in that section except "Baseline".

This will give you three separate builds, with separate behavior:

  • CodeAnalysis - This evaluates all the code contracts at build time, searching for any errors. This "build" doesn't result in a usable dll, but should be run before checking in code, similar to a unit test.
  • Debug - This turns on all code contracts at runtime. This includes code contract checks for any libraries that your library uses (such as CLR libraries). It also turns on consistency checks such as Contract.Assert and ContractInvariantMethod.
  • Release - This turns off all code contracts in your dll at runtime. However, it includes a separate ".Contracts.dll" which contains the contracts for your library's public API.

Projects consuming your library should reference your Release build. In their Debug configuration, they should check "Perform Runtime Contract Checking" and "Call-site Requires Checking"; this will ensure that the code contracts for your library's public API are enforced at runtime (the "Call-site" option uses the ".Contracts.dll" assembly that you built). In their Release configuration, they should leave code contracts disabled, which allows all assemblies to run at full speed.

If the consuming project suspects a bug in your library (i.e., their Debug build doesn't cause any Contracts violations but your library is still not behaving as expected), they can remove the reference to your Release build and add a reference to your Debug build. This is an easy way to enable all the code contract checks in your library, even the internal ones.

Preconditions (Contract.Requires)

Preconditions require some condition at the beginning of a method. Common examples are requiring a parameter to be non-null, or to require the object to be in a particular state.

public string GetObjectInfo(object obj)
{
  Contract.Requires(obj != null);
  return obj.ToString();
}

Postconditions (Contract.Ensures)

Postconditions guarantee some condition at the end of a method. It is often used with Contract.Result to guarantee that a particular method won't return null.

private string name; // never null
public string Name
{
  get
  {
    Contract.Ensures(Contract.Result<string>() != null);
    return this.name;
  }
}

Preconditions and Postconditions on Interfaces

Both preconditions and postconditions are commonly placed on interface members. Code Contracts includes the ContractClassAttribute and ContractClassForAttribute to facilitate this:

[ContractClass(typeof(MyInterfaceContracts))]
public interface IMyInterface
{
  string GetObjectInfo(object obj);
  string Name { get; }
}

[ContractClassFor(typeof(IMyInterface))]
internal abstract class MyInterfaceContracts : IMyInterface
{
  public string GetObjectInfo(object obj)
  {
    Contract.Requires(obj != null);
    return null; // fake implementation
  }

  public string Name
  {
    get
    {
      Contract.Ensures(Contract.Result<string>() != null);
      return null; // fake implementation does not need to satisfy postcondition
    }
  }
}

With generic interfaces, the same idea holds:

[ContractClass(typeof(MyInterfaceContracts<,>))]
public interface IMyInterface<T, U>
{
  string GetObjectInfo(object obj);
  string Name { get; }
}

[ContractClassFor(typeof(IMyInterface<,>))]
internal abstract class MyInterfaceContracts<T, U> : IMyInterface<T, U>
{
  public string GetObjectInfo(object obj)
  {
    Contract.Requires(obj != null);
    return null; // fake implementation
  }

  public string Name
  {
    get
    {
      Contract.Ensures(Contract.Result<string>() != null);
      return null; // fake implementation does not need to satisfy postcondition
    }
  }
}

Invariants (ContractInvariantMethod, Contract.Invariant)

Object invariants are expressed using the ContractInvariantMethod. If they are enabled by the build, then they are checked at the beginning of each method (except constructors) and at the end of each method (except Dispose and the finalizer).

public class MyClass<T, U>: public IMyInterface<T, U>
{
  private string name;

  public MyClass(string name)
  {
    Contract.Requires(name != null);
    this.name = name;
  }

  [ContractInvariantMethod]
  private void ObjectInvariant()
  {
    Contract.Invariant(this.name != null);
  }

  public string Name
  {
    get
    {
      Contract.Ensures(Contract.Result<string>() != null);
      return this.name;
    }
  }

  public string GetObjectInfo(object obj)
  {
    Contract.Requires(obj != null);
    return obj.ToString();
  }
}

Assertions and Assumptions (Contract.Assert, Contract.Assume)

There will always be some things that should be true but just have to be checked at runtime. For these, use Contract.Assert unless the static checker (i.e., the CodeAnalysis configuration) complains. You can then change them to be Contract.Assume so that the static checker can use them. There's no difference between Contract.Assert and Contract.Assume at runtime.

Reminder: if you're using the Release build setup recommended above, then all your Contract.Assert and Contract.Assume calls get removed from your release builds. So they can't be used to throw vexing exceptions, e.g., rejecting invalid input.

In the example below, the static checker would complain because Type.MakeGenericType has preconditions that are difficult to prove. So we give it a little help by inserting some Contract.Assume calls, and the static checker is then pacified.

public static IMyInterface<T, U> CreateUsingReflection()
{
  var openGenericReturnType = typeof(MyClass<,>);
  Contract.Assume(openGenericReturnType.IsGenericTypeDefinition);
  Contract.Assume(openGenericReturnType.GetGenericArguments().Length == 2);
  var constructedGenericReturnType = openGenericReturnType.MakeGenericType(typeof(T), typeof(U));
  return (IMyInterface<T, U>)Activator.CreateInstance(constructedGenericReturnType);
}

For More Information

The Code Contracts library has a thorough user manual available. It's a bit of a hard read, but they include a lot of information that I've skipped for this "intro" post, such as:

  • Specifying postconditions that hold even if the method throws an exception.
  • Techniques for gradually migrating Code Contracts into an existing library.
  • Details on how Code Contracts are inherited.
  • Contract abbreviations.
  • Applying contracts to sequences (e.g., ForAll and Exists quantifiers).
  • Advanced contract checking with Pure methods.
  • Tips for working with the static checker.

Another great source of information is Jon Skeet's updated C# in Depth - the second edition added a whole chapter just on Code Contracts.

2011-01-14

Sharp Corner: Reference Types, Value Types, and Weirdos

This week I was writing some code that had to respond differently depending on whether a generic argument was a reference type or a value type. This was bit complex, since "reference type" and "value type" do not have very specific meanings - which was actually a good thing because it forced me to consider exactly what my code requirements were, and it turned out that I was using just slightly different meanings of "reference type" in different places.

During this exploration, I developed a few tests to evaluate the type system (code at the end of this post). The results are summarized below, along with some of my thoughts on the weirdos (types which are sort-of reference and sort-of value, depending on your definition of "reference" and "value").

One way of defining a "reference type" is whether Type.IsClass is true; another way of defining a "reference type" is whether it satisfies a generic class constraint (e.g., void Test<T>() where T : class). Likewise, value types have Type.IsValueType and generic struct constraints.

The table below includes tests on a variety of types, grouped into "mostly reference types" and "mostly value types". The types that are more clearly reference/value types are at the top of each group, with the weirdos at the bottom.

Category Example Type IsClass Satisfies class Constraint IsValueType Satisfies struct Constraint Satisfies Without Constraints
Classes class Class {}          
Arrays int[]          
Delegates delegate void DelegateT();          
Interfaces interface Interface {}          
Pointers int*          
Value types int          
Enumerations enum EnumT {}          
Nullable value types int?          
Void void          

Interfaces are a Bit Weird

Interfaces return false for both IsClass and IsValueType. This makes sense, since either reference types or value types may inherit from an interface. However, interface variables may be declared and act like reference types (boxing value types as necessary), so interfaces do satisfy generic class constraints.

Take-home point: If IsClass is false but IsInterface is true, the type will still satisfy a generic class constraint.

Nullable Value Types are a Bit Weird

Nullable types return true for IsValueType, but do not satisfy generic struct constraints (nor class constraints). They can only be used as generic parameters without class or struct constraints.

Take-home point: Nullable types (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) will not satisfy a generic struct constraint, even though IsValueType is true.

Pointers are Definitely Weird

To be honest, I don't know why IsClass is true for pointers (the spec says they are, but without any reason given). They act exactly like value types, and they can't satisfy a generic class constraint. In fact, they can't be used as any kind of generic argument. This makes pointer types a corner case: they only have to be dealt with if the user is passing a Type instance rather than a generic type argument.

Take-home point: If IsPointer is true, then the type cannot be used as a generic type parameter at all (and therefore cannot satisfy a class constraint, even though IsClass is true).

Void is Definitely Weird

Void claims to be a value type (IsValueType is true) - which sort of makes sense, if we think of it as a value type that cannot have a value - but it cannot satisfy a struct constraint. In fact, like pointers, void cannot be used as a generic type argument at all. This makes void another corner case: they only have to be dealt with if the user is passing a Type instance rather than a generic type argument.

Take-home point: The void type (type == typeof(void)) cannot be used as a generic type parameter at all (and therefore cannot satisfy a struct constraint, even though IsValueType is true).

Test Code

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TypeSystemTests
{
    internal static class TypeEx
    {
        public static void NoConstraint<T>() { }
        public static void StructConstraint<T>() where T : struct { }
        public static void ClassConstraint<T>() where T : class { }

        public static bool CanBeUsedAsGenericParameter(this Type type)
        {
            try
            {
                typeof(TypeEx).GetMethod("NoConstraint").MakeGenericMethod(type).Invoke(null, null);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool SatisfiesGenericStructConstraint(this Type type)
        {
            try
            {
                typeof(TypeEx).GetMethod("StructConstraint").MakeGenericMethod(type).Invoke(null, null);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool SatisfiesGenericClassConstraint(this Type type)
        {
            try
            {
                typeof(TypeEx).GetMethod("ClassConstraint").MakeGenericMethod(type).Invoke(null, null);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool IsVoid(this Type type)
        {
            return (type == typeof(void));
        }

        public static bool IsNullable(this Type type)
        {
            return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
        }
    }

    [TestClass]
    public class UnitTests
    {
        public class Class { }
        [TestMethod]
        public void Classes()
        {
            var type = typeof(Class);
            Assert.IsTrue(type.IsClass);
            Assert.IsTrue(type.SatisfiesGenericClassConstraint());
            Assert.IsFalse(type.IsValueType);
            Assert.IsFalse(type.SatisfiesGenericStructConstraint());
            Assert.IsTrue(type.CanBeUsedAsGenericParameter());

            Assert.IsFalse(type.IsArray);
            Assert.IsFalse(type.IsEnum);
            Assert.IsFalse(type.IsInterface);
            Assert.IsFalse(type.IsPointer);
            Assert.IsFalse(type.IsVoid());
            Assert.IsFalse(type.IsNullable());
        }

        [TestMethod]
        public void Arrays()
        {
            var type = typeof(int[]);
            Assert.IsTrue(type.IsClass);
            Assert.IsTrue(type.SatisfiesGenericClassConstraint());
            Assert.IsFalse(type.IsValueType);
            Assert.IsFalse(type.SatisfiesGenericStructConstraint());
            Assert.IsTrue(type.CanBeUsedAsGenericParameter());

            Assert.IsTrue(type.IsArray);
            Assert.IsFalse(type.IsEnum);
            Assert.IsFalse(type.IsInterface);
            Assert.IsFalse(type.IsPointer);
            Assert.IsFalse(type.IsVoid());
            Assert.IsFalse(type.IsNullable());
        }

        public delegate void DelegateT();
        [TestMethod]
        public void Delegates()
        {
            var type = typeof(DelegateT);
            Assert.IsTrue(type.IsClass);
            Assert.IsTrue(type.SatisfiesGenericClassConstraint());
            Assert.IsFalse(type.IsValueType);
            Assert.IsFalse(type.SatisfiesGenericStructConstraint());
            Assert.IsTrue(type.CanBeUsedAsGenericParameter());

            Assert.IsFalse(type.IsArray);
            Assert.IsFalse(type.IsEnum);
            Assert.IsFalse(type.IsInterface);
            Assert.IsFalse(type.IsPointer);
            Assert.IsFalse(type.IsVoid());
            Assert.IsFalse(type.IsNullable());
        }

        public interface Interface { }
        [TestMethod]
        public void Interfaces()
        {
            var type = typeof(Interface);
            Assert.IsFalse(type.IsClass);
            Assert.IsTrue(type.SatisfiesGenericClassConstraint());
            Assert.IsFalse(type.IsValueType);
            Assert.IsFalse(type.SatisfiesGenericStructConstraint());
            Assert.IsTrue(type.CanBeUsedAsGenericParameter());

            Assert.IsFalse(type.IsArray);
            Assert.IsFalse(type.IsEnum);
            Assert.IsTrue(type.IsInterface);
            Assert.IsFalse(type.IsPointer);
            Assert.IsFalse(type.IsVoid());
            Assert.IsFalse(type.IsNullable());
        }

        [TestMethod]
        public void Pointers()
        {
            unsafe
            {
                var type = typeof(int*);
                Assert.IsTrue(type.IsClass);
                Assert.IsFalse(type.SatisfiesGenericClassConstraint());
                Assert.IsFalse(type.IsValueType);
                Assert.IsFalse(type.SatisfiesGenericStructConstraint());
                Assert.IsFalse(type.CanBeUsedAsGenericParameter());

                Assert.IsFalse(type.IsArray);
                Assert.IsFalse(type.IsEnum);
                Assert.IsFalse(type.IsInterface);
                Assert.IsTrue(type.IsPointer);
                Assert.IsFalse(type.IsVoid());
                Assert.IsFalse(type.IsNullable());
            }
        }


        [TestMethod]
        public void ValueTypes()
        {
            var type = typeof(int);
            Assert.IsFalse(type.IsClass);
            Assert.IsFalse(type.SatisfiesGenericClassConstraint());
            Assert.IsTrue(type.IsValueType);
            Assert.IsTrue(type.SatisfiesGenericStructConstraint());
            Assert.IsTrue(type.CanBeUsedAsGenericParameter());

            Assert.IsFalse(type.IsArray);
            Assert.IsFalse(type.IsEnum);
            Assert.IsFalse(type.IsInterface);
            Assert.IsFalse(type.IsPointer);
            Assert.IsFalse(type.IsVoid());
            Assert.IsFalse(type.IsNullable());
        }

        public enum EnumT { }
        [TestMethod]
        public void Enums()
        {
            var type = typeof(EnumT);
            Assert.IsFalse(type.IsClass);
            Assert.IsFalse(type.SatisfiesGenericClassConstraint());
            Assert.IsTrue(type.IsValueType);
            Assert.IsTrue(type.SatisfiesGenericStructConstraint());
            Assert.IsTrue(type.CanBeUsedAsGenericParameter());

            Assert.IsFalse(type.IsArray);
            Assert.IsTrue(type.IsEnum);
            Assert.IsFalse(type.IsInterface);
            Assert.IsFalse(type.IsPointer);
            Assert.IsFalse(type.IsVoid());
            Assert.IsFalse(type.IsNullable());
        }

        [TestMethod]
        public void NullableValueTypes()
        {
            var type = typeof(int?);
            Assert.IsFalse(type.IsClass);
            Assert.IsFalse(type.SatisfiesGenericClassConstraint());
            Assert.IsTrue(type.IsValueType);
            Assert.IsFalse(type.SatisfiesGenericStructConstraint());
            Assert.IsTrue(type.CanBeUsedAsGenericParameter());

            Assert.IsFalse(type.IsArray);
            Assert.IsFalse(type.IsEnum);
            Assert.IsFalse(type.IsInterface);
            Assert.IsFalse(type.IsPointer);
            Assert.IsFalse(type.IsVoid());
            Assert.IsTrue(type.IsNullable());
        }

        [TestMethod]
        public void Void()
        {
            var type = typeof(void);
            Assert.IsFalse(type.IsClass);
            Assert.IsFalse(type.SatisfiesGenericClassConstraint());
            Assert.IsTrue(type.IsValueType);
            Assert.IsFalse(type.SatisfiesGenericStructConstraint());
            Assert.IsFalse(type.CanBeUsedAsGenericParameter());

            Assert.IsFalse(type.IsArray);
            Assert.IsFalse(type.IsEnum);
            Assert.IsFalse(type.IsInterface);
            Assert.IsFalse(type.IsPointer);
            Assert.IsTrue(type.IsVoid());
            Assert.IsFalse(type.IsNullable());
        }
    }
}