Tigraine

Daniel Hoelbling-Inzko talks about programming

My very own Inversion of Control container: Pandora

Posted by Daniel Hölbling on May 21, 2009

Almost a year ago or so I listened to a .NET Rocks episode about the Castle MicroKernel/Windsor and the speaker was saying something along the lines of: “Writing a simple IoC container isn’t all that difficult, it’s basically a hashtable of types and 20 lines of code”.

I always felt that it can’t really be that easy so today I decided to try it for myself. Not so much because I want to use my own container (Windsor does everything I want it to do), but out of curiosity and to learn some things about creating classes with reflection etc.
Also I wanted a small project that I use to improve my TDD and API design skills.

So, here it is. Meet: Pandora

It’s a very lightweight IoC container that is more or less a hashtable of services with their implementing type as value.

[Fact]
public void CanResolveClassWithoutDependencies()
{
    var componentStore = new ComponentStore();
    componentStore.Add<IService, ClassWithNoDependencies>();

    var container = new PandoraContainer(componentStore);     var result = container.Resolve<IService>();

    Assert.IsType<ClassWithNoDependencies>(result); }

Pandora will look at the implementing type’s constructor and try to resolve any dependency found there (in a recursive manner). Going from the biggest constructor to the smallest.

One obvious limitation right now is that only one implementing type per service can be provided at the moment.
Also there is no detection of circular dependencies and no lifestyle management (meaning every time I call Resolve I get a new object graph).

Now, this is far away from a useable product. But writing it allowed me to look at some of the problems involved in the process and I had to learn a thing or two about reflection and generics.

One thing I wanted was to avoid having Type parameters. Having to write something like

container.AddComponent(typeof(IService), typeof(Implementor));

feels awkward every time, so I decided to go with generics to enable a syntax like this one:

container.AddComponent<IService, Implementor>();

Much cleaner and I could even add the constraint that Implementor must inherit from IService:

public void AddComponent<T, TImplementor>()
    where T : class
    where TImplementor : T
{
    componentStore.Add<T, TImplementor>();
}

Now, the problem with this is that you can’t convert a Type to a generic call afterwards. Once I needed to resolve dependencies that are of a different type I couldn’t simply write Resolve<myType>().
To invoke a generic method in the current instance with a Type argument you need to use generics to create that method, like this:

MethodInfo method = typeof (PandoraContainer).GetMethod("Resolve");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null)

So, it works but I guess it’s not the best way to regularly use reflection to call methods, so I went back to Type parameters and created overloads for the generics. (And now I see why Windsor has Type parameter overloads for everything ;))

Pandora is open source and licensed under the Apache License 2.0. I strongly suggest you do not use this anywhere near production systems but strictly for educational purposes.

comments powered by Disqus

My Photography business

Projects

dynamic css for .NET

Archives

more