About me

Michael L Perry

Improving Enterprises

Principal Consultant

@michaellperry

User login

Immutability

The constructor has a very powerful contract, and one that the compiler proves. The constructor is called once and only once.

We can use this promise to prove some very useful things. We can prove that required properties are set. We can prove that A happens before B (as we can with other prerequisite techniques). But more strongly, we can prove that A does not happen after B.

Required properties
A constructor has to be called. There is no other way to get an instance of an object. If there are any required properties, they should be constructor parameters. Otherwise, there is no way to prove that they've been set.

class ReportRequest
{
    // Required parameters.
    private User _requestedBy;
    private Company _requestedFor;

    // Optional parameters.
    private DateTime? _fromDate;
    private DateTime? _toDate;

    public ReportRequest(User requestedBy, Company requestedFor)
    {
        _requestedBy = requestedBy;
        _requestedFor = requestedFor;
    }

    public User RequestedBy
    {
        get { return _requestedBy; }
    }

    public Company RequestedFor
    {
        get { return _requestedFor; }
    }

    public DateTime? FromDate
    {
        get { return _fromDate; }
        set { _fromDate = value; }
    }

    public DateTime? ToDate
    {
        get { return _toDate; }
        set { _toDate = value; }
    }
}

We can prove that the user requesting the report and the company for which the report is requested are specified. The filter parameters are optional.

Immutable properties
Once a constructor is called, it cannot be called again. This is an extremely powerful contract, and can be used to prove that properties can't change. In the above example, the RequestedBy and RequestedFor properties are immutable. They can only be set by the constructor, which can only be called once.

Immutability is an example of the statement A does not happen after B. A) the property changes. B) the property is set. The property does not change after it is set.

There are other statements of this form that the constructor can prove. For example, a connection string cannot change after a database connection has been established. Here's a snippet of the ADO.NET SqlConnection class:

public class SqlConnection
{
    public SqlConnection();
    public SqlConnection(string connectionString);
    public string ConnectionString { get; set; }
}

Do you see the problem? You cannot prove that the connection string does not change. This class has guard code that throws an exception if you do so. It would be a simple change to make this contract provable:

public class SqlConnection
{
    public SqlConnection(string connectionString);
    public string ConnectionString { get; }
}

Don't waste the constructor
Using a constructor to prove a contract is a powerful capability. It is weakened when the constructor is used for other things.

public class User
{
    private string _userId;
    private string _firstName;
    private string _lastName;

    public User(string userId) :
        this(userId, string.Empty, string.Empty)
    {
    }

    public User(string userId, string firstName, string lastName)
    {
        _userId = userId;
        _firstName = firstName;
        _lastName = lastName;
    }

    public string UserId
    {
        get { return _userId; }
    }

    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }

    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }
}

This class has a weak constructor. The name of a user can change, but the ID cannot. An overloaded constructor initializing the name is provided for convenience. But this convenience comes at a price. It is more difficult to see that the user ID is required and immutable. It's still provable, but that information is not called out.

Don't overload the constructor. Don't use it for convenience. Don't initialize mutable properties. When a constructor is used only to set required and immutable properties, the intent is clear, and the proof is easy.