Table of Contents

Writing a Minimal IoC Container in C#


Coaching is fun and satisfying for me. I’ve learned that young engineers learn better by doing.

I wanted to illustrate the difference between Inversion of Control and Dependency Injection. Writing a minimal container turned out to be a great way to illustrate these concepts.

I was amazed as to how “natural” the whole thing turned out to be.

I was coaching an intern a few months ago. It’s not the first time, I find it fun to guide new people into the field. I give them shortcuts about what to learn and how to do quickly.

I talked to him about Inversion of control (IoC) and Dependency Injection (DI). I thought it would be fun to write an IoC container as small as possible. It’s easier to understand by doing it, and the making of it makes obvious why dependency injection is useful.

I had to do my research. Simple is not always easy.

Heading

Even though they are related, IoC is a broader than DI. IoC means that other code calls yours; DI goes beyond that and implements IoC by using composition.

In IoC a framework controls how modules are instantiated and managed.

DI solves problems of how an application can be independent of how its objects are created. This is very useful when you need a configurable application or when unit testing involving mock objects. DI helps you to change application more easily as it grows in size and complexity.

A DI or IoC container needs to instantiate objects (dependencies) and provide them to the application. It needs to deal with,

  • Constructor injection
  • Setter injection
  • Interface injection

The container needs to automatically instantiate objects of specific types and pass them in the constructors, setter methods, or interfaces to classes. I’ll focus on constructor injection since is the most common.

The container will support registering concrete types for interfaces and it will resolve them when requested by the user. It will auto resolve the constructor parameters as well.

An Example

I wrote an interface I called IWriter, including a Write method. That IWriter interface is implemented by a Writer concrete class. You register that in the container when initializing your application. Later, the app can request an IWriter instance to the container.

container.Register<IWriter, Writer>(); ... ... Writer writer = container.Create();

I found this post from Ayende where he wrote an IoC container in only 15 lines of code. Pretty impressive in my opinion.

His post is old and I believe that we can do better with the help of generics and reflection. So I modified Ayende’s code and I came up with an IoC container in 11 lines of code. For one thing, it doesn’t use delegates to instantiate objects.

This minimal IoC container does type checking and ensures that registered types implement the interfaces/classes that they are registered with. Generics are used to accomplish it.

It also auto-instantiates objects for the types registered. It resolves constructor parameters and auto instantiates them when resolving types.

public class MinimalContainer
{
private readonly Dictionary<Type, Type> types = new Dictionary<Type, Type>();

public void Register<TInterface, TImplementation>() where TImplementation : TInterface
{
types[typeof(TInterface)] = typeof(TImplementation);
}

public TInterface Create()
{
return (TInterface)Create(typeof(TInterface));
}

private object Create(Type type)
{
//Find a default constructor using reflection
var concreteType = types[type];
var defaultConstructor = concreteType.GetConstructors()[0];
//Verify if the default constructor requires params
var defaultParams = defaultCtor.GetParameters();
//Instantiate all constructor parameters using recursion
var parameters = defaultParams.Select(param => Create(param.ParameterType)).ToArray();

return defaultConstructor.Invoke(parameters);
}

}

This is how to use the container.

var container = new MinimalContainer();

container.Register<IWelcomer, Welcomer>();
container.Register<IWriter, ConsoleWriter>();

var welcomer = container.Create();
welcomer.SayHelloTo("World");

Here are the classes used by the example above,

public interface IWelcomer {
void SayHelloTo(string name);
}

public class Welcomer : IWelcomer
{
private IWriter writer;

public Wey(IWriter writer) {
this.writer = writer;
}

public void SayHelloTo(string name)
{
writer.Write($"Hello {name}!");
}
}

public interface IWriter {
void Write(string s);
}

public class ConsoleWriter : IWriter
{
public void Write(string s)
{
Console.WriteLine(s);
}
}

This is NOT production ready, obviously. Note that there is no config file: this container only support constructor injection and registration in the code. A complete IoC container would support way more features such other types of injections, registration in configuration files, and managing the object life-cycle.

I recommend you use full blown containers such AutoFac, Ninject or StructureMap when writing production code. I found this little task fun to do though, and it’s easier to show to less experienced developers to learn the difference between IoC and DI in as simple a way as possible.

Learn More about Encora

We are the software development company fiercely committed and uniquely equipped to enable companies to do what they can’t do now.

Learn More

Global Delivery

READ MORE

Careers

READ MORE

Industries

READ MORE

Related Insights

Enabling Transformation in Hospitality through Technology-Led Innovation

As the exclusive sponsor of the 2024 Hotel Visionary Awards, we support organizations leading ...

Read More

Key Insights from HLTH 2024: The Future of Patient-Centered Healthcare

Discover key insights from HLTH 2024 on digital health, AI in diagnostics, data interoperability, ...

Read More

Data-Driven Engineering: Transforming Operations and Products from Insight to Impact

Discover how data-driven engineering transforms operations and product development, enhancing team ...

Read More
Previous Previous
Next

Accelerate Your Path
to Market Leadership 

Encora logo

Santa Clara, CA

+1 669-236-2674

letstalk@encora.com

Innovation Acceleration

Speak With an Expert

Encora logo

Santa Clara, CA

+1 (480) 991 3635

letstalk@encora.com

Innovation Acceleration