Policy-based class design in C#

A couple of years ago I read Andrei Alexandrescu’s book “Modern C++ Design” and was quite impressed with what he did there with C++ templates. His policy-based class design approach is an elegant and fascinating way of writing classes (respectively libraries) which can support every kind of user desired behavior by basically assembling classes out of little code snippets called “policies”. In his book he does that by specifying policies as type parameters and having a host class deriving from them (Please have a look at the wikipedia article here or his book for more details on that). Recently I have been thinking about policy-based design again and became interested in using this programming paradigma in C#.

Policies in C++ vs. Policies in C#

Let’s assume we want to create a class which can cache a value of type T and depending on a certain invalidation policy InvalidationPolicy the cache should no longer hand out the cached but ask for the latest value. In the original C++ policy-base design such a class might look like:

template<class T, template<class> class InvalidationPolicy>
class CacheOf : public InvalidationPolicy<T>
{
public:
    T GetValue()
    {
        if (!IsValid())
        {
            _value = GetLatestValue();
        }
        return _value;
    }
private:
    T _value;
};

As we can see the class CacheOf derives from its invalidation policy – a feature which is not available in C# as you cannot derive from generic type parameters. Unfortunately one of the key features in Alexandrescu’s approach is that the host class can be equipped with policies at compile time meaning that the desired behavior is baked into a brand new class (since the host is deriving from the policies) everytime a user specifies a new combination of policies.

Now imaging that the cache needs to be enhanced by a second policy which is responsible for retrieving the latest values. The CacheOf class needs to derive from this second policy as well – again a feature which is not available in C# as C# does not support multiple inheritance.

This first analysis is a bit disappointing because C# is missing two key features necessary which would be necessary to implement the original policy-based class design concept.

Strategy to the rescue

Not giving up so early, we have a look into the strategy pattern (as explained here) which has been described as the runtime relative of the policy-based design. Instead of generating new classes with every combination of policies, there is one compiled host class which takes 1 to n implementations of different strategies at runtime. By making them non exchangable once the host class is constructed we can come quite close to that aspect of  C++ version of policy based design.

public class CacheOf<T>
{
    private readonly IInvalidationPolicy _invalidationPolicy;

    private T _value;

    public CacheOf(IInvalidationPolicy invalidationPolicy)
    {
        _invalidationPolicy = invalidationPolicy;
    }

    public T GetValue()
    {
        if (!_invalidationPolicy.IsValid())
        {
            _value = GetLatestValue();
        }
        return _value;
    }
}

Maybe even better than the C++ method, we avoid code bloat (under the compiler hood) since whatever combination of the host and strategies is chosen no new class has to be baked and compiled.

Handy Generics?

While the C++ policy based class design relies on templates to substitute the policies, the strategy pattern based C# code above does not use generics. However, it is possible to use generics for creating the policies as well:

public class CacheOf<T, TInvalidationPolicy> 
    where TInvalidationPolicy : IInvalidationPolicy, new()
{
    private readonly IInvalidationPolicy _invalidationPolicy;

    public CacheOf()
    {
        _invalidationPolicy = new TInvalidationPolicy();
    }
}

Unfortunately this approach has one major disadvantage compared to the C++ version. In C++ it is possible to write:

template<typename int TExpirationTimeInSeconds>
class ConcretInvalidationPolicy
{
public:
    bool IsValid()
    {
        return TExpirationTimeInSeconds > _timeElapsed;
    }
private:
    int _timeElapsed;
};

And using like:

CacheOf<std::string, ConcretInvalidationPolicy<5> > cache;

(Please note that CacheOf has a different signature this time. That is because template parameter T is not of interest for the policy)

In C++ template parameters can be concret values instead of types. Something that is not at all supported in C#. For policies which require parameters for construction the way over C# generics and the new() constraint simply does not work. The conclusion is that you cannot use generics to specify the policies unless you want to follow a hybrid way where policies which do not require parameters are specified as generics and the rest is passed in as parameters for the constructor of the host class. An approach I personally don’t like as it becomes confusing and unclear to the user why some policies are specified as generics and others are not.

Accessing Policies

Obviously deriving from policies makes it possible to access policy functionality directly from the host class (assuming the right access modifiers and inheritance relationship). So for example changing a parameter a policy is working with is no problem in C++. However doing so requires the user to exactly know what assembled type he is working with. Something that might not be desirable especially when he wants to pass it around as other parties might not be interested in the concret policies used. Implementing policies as runtime strategies in C# does not stand back here.

Summary

Let’s summarize our observation for now:

  • Policy-based design is the hardwired strategy pattern design (at compile time in C++)
  • A C# class cannot derive from more than one base class (no multiple inheritance)
  • A C# class cannot derive from generic parameters
  • A C# generic cannot be a concret value in order to configure a policy
  • Implementing a policy-based designed class in C# must rely on strategies. At the latest when policies should be configurable
  • Accessing policies directly through the host class is not desirable. C# has no disadvantage here.

Recently so called mixins (wiki) for C# came into fashion. They enable C# programmers to have some sort of emulated multiple inheritance by cleverly using interfaces and extension methods. Even though being an exciting idea we will first evaluate the possibilites the strategy pattern offers and might have a look at mixins later.

Flattr this!

Leave a Reply

Your email address will not be published. Required fields are marked *