Building IoC container cSharp example

Feb 5, 2013 00:00 · 783 words · 4 minute read Programming Design pattern

What is IoC?

IoC is referring to inversion of control which means, instead of asking our supper class match themselves with subclasses , ask subclasses to adopt themselves with super class by implementing their interface.

For instance forcing all electrical devices to use the apple port to charge themselves rather than building different charger for devices.

Some definition

DIP: Dependency Inversion principal refers to the idea of inversion of control

IoC :Refer to the pattern that we use to inverting the control

DI: dependency Injection refers to implementation of the pattern but not necessarily could guide us to achieve our goal.

What is IoC container ?

It is a framework to do configure the dependencies automatically and inject them into the subclass tree.

Example

What we have?

We have a printer class that accept the printer type and do the printing according to the passed type.

print class

public class Print {
        private readonly ITypeOfPrinter _typeOfPrinter; 
        public Print(ITypeOfPrinter typeOfPrinter) {
            _typeOfPrinter = typeOfPrinter;
        } 

        public string print() {
           return _typeOfPrinter.Printing();
        }
    }

The interface of printer type is simple and have only one method to do the printing

interface

public interface ITypeOfPrinter {
        string Printing ();
}

There are currently two types of printer , colourful and black&White.

colorfull printer

public class ColorfullPrinter : ITypeOfPrinter {
	public string Printing() {
            NumberOfPrints++;
            return string.Format("it is colorfull printing {0}",NumberOfPrints);
        }

        public int NumberOfPrints { get; set; }
	}

Which are pretty much similar in their implementation

Black&White Printer

public class BlackAndWhitePrinter : ITypeOfPrinter {
        public string Printing() {
            NumberOfPrints++;
            return string.Format("it is Black and white printing {0}",NumberOfPrints);
        }
        public int NumberOfPrints { get; set; }
}
Using the class normally

Normally to calling this kind of class we have to pass the dependency into it and then call the desired method on it .

For instance if we wish to have colourful printing we passing the colourful printer like below.

using class normally

[Test]
public void Given_ColorfulPrinterType_When_Print_Then_ItShouldBeColorful() {
            var print=new Print(new ColorfullPrinter());
            string printedText = print.print();
            Assert.IsTrue(printedText.Contains("colorfull"));
}
Using Resolver

To make our method more single responsible we can pass the responsibility of making dependency into another class and ask it to decide what type of printer we need in each case.

Resolver class

public ITypeOfPrinter ResolvePrinterType() {
            ITypeOfPrinter typeOfPrinter;
            var random = new Random();
            if (random.Next(2) == 1) {
                typeOfPrinter = new ColorfullPrinter();
            } else {
                typeOfPrinter = new BlackAndWhitePrinter();
            }
            return typeOfPrinter; 
}
Using IoC container

If our class constructor has too many dependencies or require variable building we can make an IoC container to help us to register our dependencies easier.

In this case we can have Resolve method that accept generic type and return the appropriate generic type.

Resolver method

public T Resolve<T>() {
            return (T)Resolve(typeof(T));
}

It is looking into our dictionary and find the desired type of it.

Resolve object

private object Resolve(Type type) {
            Type resolveType=null;       
            try {
                resolveType = dictionaryType[type]; 
            } catch {
                throw new Exception(string.Format("The requested type {0} is not in registered dictionary",type));
            }         
 
            ConstructorInfo firstConstructorInfo = resolveType.GetConstructors().First(); 
            ParameterInfo[] constructorParameters = firstConstructorInfo.GetParameters();
            if(!constructorParameters.Any()) {
                return Activator.CreateInstance(resolveType);
            }
 
            var resolveParameterList= constructorParameters.Select(parameterToResolve => Resolve(parameterToResolve.ParameterType)).ToList();
 
            return firstConstructorInfo.Invoke(resolveParameterList.ToArray());
}

We seek for the first constructor in this example and try to retrieve all of the input variable of it. If the constructor does not have any input variable it create an instance of it. Otherwise go to all the input variable and make an instance of it and add it to the dictionary.Finally we can invoke the constructor with its input variables and return it.

Registration

The last step is to register our dependencies. We have to map the appropriate called type to the output type by adding them to our dictionary.

Registering

public void Register<TFrom, TTo>() {
            dictionaryType.Add(typeof(TFrom),typeof(TTo));
        }

For instance we should map ITypeOfPrinter to Colorfull printer class.

Calling it

In order to call it we have to register the dependencies by mapping Print to Print and Interface to the desired concrete class.

calling the resolver

[Test]
public void Given_Nothing_When_Print_Then_ItShouldFindAppropriateType() {
            var resolver = new Resolver();
            resolver.Register<Print, Print>();
            //resolver.Register<ITypeOfPrinter, ColorfullPrinter>();
            resolver.Register<ITypeOfPrinter, BlackAndWhitePrinter>();
            var resolvedPrinter = resolver.Resolve<Print>();           
            string printedText = resolvedPrinter.print();
            Assert.Pass(printedText);
}

And ask the resolver to resolve it and return instances of it to us.

What will be happening?

By registering type we add those key and value to our dictionary, therefore when it tries to resolve it will pass Print class to the method. Our constructor has one input and it will recursively pass it to the Resolve method to make the instance of it as well.By considering what is our registered type we can have colorfull or Black&White printing.

Download

Feel free to download the full source code of building IoC container from my github.