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!