Thursday, July 22, 2010

C# Specializations by Traits I


C# Specializations by Traits I

Since version 2.0, C# supports generics. Really a great feature. Generics are a bit comparable to C++ templates. Generics enable a C# programmer to do generic programming. But alas, specialization is not supported by default. However, with some tricks you can get quite close. In fact, you can do it, you can use specialization in C#.

Generics in C# are defined like, e.g. MyClass<T> and can be instantiated then as MyClass<int> for the integer case, MyClass<double> for the double case, etc. Generics can be used at the class level and at the method level.

What is specialization? Many C# programmers will ask. With specializations you can create different implementations for (more) specific instances of a class. There is class specialization and there is function specialization. We will work this out in detail below for C#.

What are traits classes? Traits classes are associating information with a compile-time entity. Stated otherwise, traits add another level of indirection. And by adding a level of indirection, most problems can be solved. We will see traits below.

Use case

So let's first define what we want. (By researching how to C# specializations would look like in a Generic Geometry Library, I encountered this link and my sample is a bit based on it).  We have a class:
class MyClass<T>
{
    public string Foo() { return "MyClass"; }
}

And we want to do something this with this class:
            var cls1 = new MyClass<int>();
            var cls2 = new MyClass<double>();

            string id = cls1.Bar();
            string dd = cls2.Bar();

Where, and that is important here: the Bar() method gives a different result for  MyClass<int> then for MyClass<double>. So, MyClass is specialized for MyClass<int> in another way as for MyClass<double>. There is no inheritance here; MyClass<int> is not inherited from MyClass<T>, but is a specific case of MyClass<T>.

Extension methods

With extension methods, another really great new feature of C#, we can do this.

        public static string Bar1<T>(this MyClass<T> obj)
        {
            return "*";
        }
        public static string Bar1(this MyClass<int> obj)
        {
            return "i";
        }
        public static string Bar1(this MyClass<double> obj)
        {
            return "d";
        }

This appliance of extension methods for specialization is described here, in a similar example. The Bar1 method, as it is now called, is an extension method, and results in different results for the int case and the double case. This is already great.

Alas, as soon as called within another generic function, for example:
        public static void CallBar1<T>(MyClass<T> cls)
        {
            string s = cls.Bar1();
        }



it will always call the generic one, returning a *. If we remove that generic one, we get compiler errors:
Error    1    Instance argument: cannot convert from 'MyClass<T>' to 'MyClass<int>' 
Error    2    'MyClass<T>' does not contain a definition for 'Bar1' and the best extension method overload 'Bar1(StackOverflow.MyClass<int>)' has some invalid arguments   

Whatever we try here, it will not work, as is described here on the same page. Alas. In C++ this would work. We need this to work, otherwise it is of low usage.

Traits classes

So we try another approach by adding a level of indirection. This turns out to be, based on our good experiences in C++, a traits class. It reads like this:

    interface BaseTraits<T>
    {
        string Apply(T cls);
    }

    class IntTraits : BaseTraits<MyClass<int>>
    {
        public string Apply(MyClass<int> cls)
        {
            return cls.Foo() + " i";
        }
    }

    class DoubleTraits : BaseTraits<MyClass<double>>
    {
        public string Apply(MyClass<double> cls)
        {
            return cls.Foo() + " d";
        }
    }


We see here a generic interface, BaseTraits<T>, and two derived classes implementing that interface for specific cases of MyClass. They are completely different classes. The only thing is that they implement the same interface. Their method Apply does something with that class, here returning a string.

We implement a new generic extension method, Bar2, there is only one version of it now. It takes both the object and traits as an argument. It calls the apply method of the traits, specifying the object:

        public static string Bar2<T>(this T obj, BaseTraits<T> traits)
        {
            return traits.Apply(obj);
        }


Bar2 can be called like the following:
string sb21 = cls1.Bar2(new IntTraits());
string
sb22 = cls2.Bar2(new DoubleTraits());


and it now works from a generic method. So that is the difference with the previous CallBar1: that one didn't work. This one works, because we have added a level of indirection, the traits class.       

public static void CallBar2<T>(T cls, BaseTraits<T> traits)
{
    string s = traits.Apply(cls);
}


CallBar2(cls1, new IntTraits());

In both cases (int / double) the correct Apply method is taken. Of course it is... Because the traits to call is specified explicitly... It is just inheritance, nothing new. Besides this, I think it is considered as ugly to always mention the traits class so explicitly...

To make up the balance, this traits give:
  • a call to Bar2 which is quite ugly
  • but the traits system is still quite powerful! 
  • and this traits system is the key to success! 
Read on.

IDictionary<Type, object>

In C++, traits classes are used for generic programming, as e.g. in tag dispatching (my favourite). Traits are (I repeat) associating information with a compile-time entity. The right implementation (information) is found by the compiler automatically by the compile-time entity (the class). It seems to feel the type automagically and performs the right action (defining a type, calling a method).

As mentioned, in C# this is not possible, it is just not implemented, specialization is not implemented. However, we can associate information between types and traits using a dictionary.

So the 'trick' (we talked about auto-magic) is a dictionary of types and traits classes... The directionary-key is a Type, its value is a traits. Let's call it a specialization register, traits register, or just a register. So we add:
public static IDictionary<Type, object> register;


... and we fill the register once, for example at the start of program:
register = new Dictionary<Type, object>();
register[typeof(MyClass<int>)] = new IntTraits();
register[typeof(MyClass<double>)] = new DoubleTraits();


... and then we use this register in our extension method Bar to find the right traits:
public static string Bar<T>(this T obj)
{
    BaseTraits<T> traits = register[typeof(T)] as BaseTraits<T>;
    return traits.Apply(obj);
}


... and we can use it now without referring to the traits:
            var cls1 = new MyClass<int>();
            var cls2 = new MyClass<double>();

            string id = cls1.Bar();
           string dd = cls2.Bar();

This was our use case! It will call the right specialization. Magic! Also if you call it from a generic method, it takes the right specialization. We have implemented specialization in C#. We've done it!

A few notes

  • Don't think this as an academic exercise. It has thousands of use cases. It is really a great design pattern. Let's call it Specialization by Traits
  • Don't think this is common place. I googled for several combinations of specialization, dictionary<Type, object>, traits,extension methods, and google is not holy but still your friend; I did find many reports that specialization is not possible, but nowhere I saw any mentioning of this system. Close is this and this where a similar register is used, but for different purposes, and this one which is quite close
  • We can simplify the calls to register by making it a private singleton and adding a method, see below for the end version
  • We use here the extension method and call it as it if were a method of MyClass<T>. It is also possible to use it in a normal method (of course), for example to calculate the distance between two points, in the next Blog
  • Depending on the use case, the register of traits can also be a register of delegates, and registering can be done using a lambda function. The traits class then takes the form of a delegate. We wil see more of this.
  • Of course the dictionary will influence performance. So contrary to C++, where specialization and inline methods will boost performance, it will be a little bit slower here. Normally, no problem, I estimate. We will come back to this in another blog.


So, to be complete, we list the full source here, including the main program with the more appropriate calls to the dictionary.

using System;
using System.Collections.Generic;


namespace StackOverflow
{
    class MyClass<T>
    {
        public string Foo() { return "MyClass"; }
    }

    interface BaseTraits<T>
    {
        string Apply(T cls);
    }

    class IntTraits : BaseTraits<MyClass<int>>
    {
        public string Apply(MyClass<int> cls)
        {
            return cls.Foo() + " i";
        }
    }

    class DoubleTraits : BaseTraits<MyClass<double>>
    {
        public string Apply(MyClass<double> cls)
        {
            return cls.Foo() + " d";
        }
    }

    static class Specialization
    {
        private static IDictionary<Type, object> register;

        public static void Associate<T>(object traits)
        {
            if (register == null)
            {
                register = new Dictionary<Type, object>();
            }
            register[typeof(T)] = traits;
        }
        public static BaseTraits<T> Associated<T>()
        {
            return register[typeof(T)] as BaseTraits<T>;
        }
    }

    
    static class Program
    {
        public static string Bar<T>(this T obj)
        {
            return Specialization.Associated<T>().Apply(obj);
        }

        public static string CallBar<T>(MyClass<T> cls)
        {
            return cls.Bar();
        }

        static void Main(string[] args)
        {
            Specialization.Associate<MyClass<int>>(new IntTraits());
            Specialization.Associate<MyClass<double>>(new DoubleTraits());

            var cls1 = new MyClass<int>();
            var cls2 = new MyClass<double>();

            string id = cls1.Bar();
            string dd = cls2.Bar();
            string ii = CallBar(cls1);
            string di = CallBar(cls2);
        }
    }
}


In the next blogs we will see how this is really useful, in real use cases:
  • geometry...
  • object relational modelling...
  • filling Silverlight lists...
Stay tuned!

1 comment:

  1. Very useful! Just what I was looking for. I would suggest a change of the examples to use more meaningful names: foo / bar does not say much and makes it easy to get lost.

    ReplyDelete