Inversion of Control
In classic software design, a central module or object may instantiate its own dependencies and call them as required to perform a task. This leads to tightly coupled designs that can be inflexible and that complicate the task of replacing dependencies or substituting them with alternative objects during unit testing.
Inversion of Control (IoC) is a programming principle that reverses the flow of control of a solution or system, removing the centralised control. When using the inversion of control principle, classes are decoupled from their dependencies. Each class or component may then be responsible for performing a very specific and focussed task, possibly using dependencies that are assumed to be interchangeable. Dependencies are usually provided using dependency injection or the service locator design pattern.
IoC containers are used to simplify the provision of a dependant class's dependencies. In the most basic form, an IoC container is an implementation of the service locator design pattern that permits pre-instantiated objects to be registered with the container and later extracted. To substitute a new class and modify the operation of an entire solution, only the initial registration of the type need be changed.
Some IoC containers allow you to register types, rather than objects, and instantiate the type when requested, possibly using parameters. Others allow the registrations to be deserialised from an XML file, which could be modified in a text editor. In this article we will create a basic IoC container that does not include these additional features. This will demonstrate the use of IoC containers with the simplest possible code. You may decide to enhance the code to add extra features for your own use. However, you should also consider obtaining one of the many alternative IoC container solutions that are currently available.
Implementing the IoC Container
The UML class diagram above shows the structure of the inversion of control container class that we will create in the remainder of the article. The members of the class are:
- registeredTypes. This private field is the dictionary that stores all of the registered types and their matching objects. In most cases the type registered will be an interface and the object will implement that interface. This will allow the concrete implementation to be switched easily.
- Register. This generic method is used to link a type to a service object. The type being registered is provided in place of the "T" type parameter and the object is provided as the only argument.
- Resolve. This method is used to request an object of a given type. The required type, usually an interface, is provided to the type parameter.
The following is the entire implementation of the above basic IoC container using C#. Note that it is implemented as a static class that uses generics. If you are using version 1.1 of the .NET framework you can create a similar IoC container without these features. To do so, you would need to implement the class as a singleton and use standard parameters instead of type parameters. Additionally, the return value of the Resolve method would need to be cast to the desired type wherever used.
public static class IoC
static Dictionary<Type, object> _registeredTypes = new Dictionary<Type, object>();
public static void Register<T>(T toRegister)
public static T Resolve<T>()
The code is quite simple. First the dictionary is defined and initialised in a single line of code. The Register method simply adds an item to this dictionary, providing the type being registered as the key and the object as the value. The Resolve method extracts the item with the given key from the dictionary. As this is a simple object, it is cast to the desired type before being returned.
Testing the IoC Container
To test the IoC container we can create an interface and a class that implements it. We will later register an instance of the class for the interface's type. The code for a simple interface and class that is used to show a message are as follows:
public interface IMessager
void ShowMessage(string msg);
public class Messager : IMessager
public void ShowMessage(string msg)
We can now register the interface's type and provide a new Message object, which will be returned whenever the type is resolved. You would usually do this when your application is first started. In this example we will only register one type. In a real solution you can register as many types as are required for the operation of your software.
Once registered, any code that has access to the IoC class can resolve the type. In the following sample the first line requests the object from the IoC container that implements IMessager. This returns the previous registered Messager object. The object is then used to output a message to the console.
IMessager messager = IoC.Resolve<IMessager>();
messager.ShowMessage("Hello, world!"); // Outputs "Hello, world!"
21 October 2010