Project musings

LeafIoC – simple Inversion of Control

If you ever wondered how Inversion of Control tools like StructureMap, NInject, LinFu, Windsor, Unity, Castle, &c are implemented in C# you’ve come to the right place. This is the first of a several exploratory blog posts on this topic.

The simplest use case is getting an instance of an interface where the implementing class has a public, parameterless constructor. This is a good place to start exploring. Consider the following code:

var cache = Container.GetInstance<ICache>();

What do we want to happen when we call GetInstance? It should return a new instance right? How does it know what to return? These are good questions and the answers are… it depends. What if we want the returned instance to be a singleton? Should the system “discover” the appropriate implementation by following a naming convention or should the programmer have to tell it everything. What if we want to change behavior at runtime? What if we want one thing if we’re running in a web environment and a different thing we we’re running in a windows application? This is just a sample of the various usage options available in the various IoC containers.

For now lets keep it really simple and say that the container should return a new instance every time and that the developer has to explicitly configure the returnable implementations. I’ll call this Leaf IoC because the implementing class doesn’t have any dependencies.

Before we can tackle GetInstance we first have to figure out how configuration should work because we won’t be able to test GetInstance without that ability. Let’s think about the test for GetInstance for a minute. First a failing test that describes the behavior we want (using the FluentAssert BDD framework):

As you can see we’ll be storing the configuration information in an internal dictionary so that we can find it quickly. We have another test to add for IsConfigured but it requires that Configure exist so let’s jump back to the tests for that method:

Notice the if statement. What should happen if we’ve already configured the system for a particular TInterface? Let’s say it should replace the existing configuration. How will we test that? We’ll need GetInstance so let’s get the happy path for that working: