Monday, November 11, 2013

Creating an IoC Container - Handling Dependencies

This is the second part of a series on creating an IoC Container. The first part dealt with the container itself, specifically the registration and resolution of types. In this part, I am going to extend the container's resolution capabilities enabling it to create instances of types that receive dependencies via their constructor.

Reflection and Recursion

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private List<object> ResolveConstructorDependencies(Type type)
{
  var constructorInfo = type.GetConstructors().First();
  var parameterInfos = constructorInfo.GetParameters();

  var constructorParameters = new List<object>();

  foreach (var parameterInfo in parameterInfos)
  {
    object parameterInstance = Resolve(parameterInfo.ParameterType);

    constructorParameters.Add(parameterInstance);
  }

  return constructorParameters;
}

Using reflection, the requested type's constructor is retrieved along with its parameters. If the type has more than one constructor, the first one will be used. If you are interested in how to handle multiple constructors, I encourage you to look at an open source IoC containers as they have many solutions for this.

With the dependencies identified, a simple iteration over this collection is performed where they are resolved. Resolution is performed in a recursive manner (by calling Resolve()) so if any of these dependencies have dependencies of their own, they too will be instantiated and so forth. Once all the dependencies have been resolved, they are returned as a collection.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private object Resolve(Type requestedType)
{
  var registration = GetRegistration(requestedType);

  object instance = null;
  if (registration != null)
  {
    var constructorParameters = ResolveConstructorDependencies(
      registration.ConcreteType);

    instance = Activator.CreateInstance(registration.ConcreteType, 
      constructorParameters.ToArray());
  }

  return instance;
}

This collection is passed to the Activator, which will use them when instantiating the requested type.

In the next part I will look at how you can create types as singletons.

A complete project containing all the source code for the container and a set of unit tests can be found here.