BarbarianIOC : A simple IOC Container






4.99/5 (39 votes)
A simple IOC Container just for the fun of it really
Article demo code : BarbarianIOC.zip
- Introduction
- What Does It Do
- How Does It Work
- That's It
Introduction
At work I have used a variety of IOC Containers including
- MEF : http://mef.codeplex.com/ which is now freely available as part of .NET in the System.Composition namespace
- Castle : http://www.castleproject.org/
And I have used others such as Unity/StructureMap/AutoFac. They are all very good and very rich.
For those of you who don't know what IOC stands for, it stands for Inversion Of Control. Which is described as follows:
In software engineering, inversion of control (IoC) is a programming technique, expressed here in terms of object-oriented programming, in which object coupling is bound at run time by an assembler object and is typically not known at compile time using static analysis.
In traditional programming, the flow of the business logic is determined by objects that are statically assigned to one another. With inversion of control, the flow depends on the object graph that is instantiated by the assembler and is made possible by object interactions being defined through abstractions. The binding process is achieved through dependency injection, although some argue that the use of a service locator also provides inversion of control.
In order for the assembler to bind objects to one another, the objects must possess compatible abstractions. For example, class A may delegate behavior to interface I which is implemented by class B; the assembler instantiates A and B then injects B to A.
In practice, inversion of control is a style of software construction where reusable code controls the execution of problem-specific code. It carries the strong connotation that the reusable code and the problem-specific code are developed independently, which often results in a single integrated application. Inversion of control as a design guideline serves the following purposes:
- There is a decoupling of the execution of a certain task from implementation.
- Every module can focus on what it is designed for.
- Modules make no assumptions about what other systems do but rely on their contracts.
- Replacing modules has no side effect on other modules.
- Inversion of control is sometimes facetiously referred to as the "Hollywood Principle: Don't call us, we'll call you", because program logic runs against abstractions such as callbacks.
Wikipedia : up on date 25/02/2013
Thing is I have always wanted to try and make one of these myself, just to see what is involved. I did not want to go too nuts on this, and just wanted the following really:
- Instance configuration : singleton / transient
- Simple registration process, maybe some sort of fluent interface
- Use the Expression API to compile into delegates for quick creation of objects
- Constructor / property injection
- Provide the ability to accept non IOC held constructor parameters
So those couple of points are ALL I wanted to get working. As I say there are a whole slew of full fledged IOC containers out there (where I have named a few above), this articles container is more of a learning exercise, that I thought I would share, in case anyone else is interested in this sort of thing.
I am calling my container BarbarianIOC
as the existing
containers all seems to have these short snappy names, and it's kind of play on
my name, and if you saw me without a shave I do kinda look a bit like a
barbarian.
So there we go. That's essentially what this article is about, but just before we get into the guts of it, please read this important note below.
IMPORTANT NOTE
I should point out that you should stick to using one of the major IOC containers out there, as this was an exercise to see what you needed to do to create your own. That is not to say I am not happy with it, I totally am, and I think with more tinkering, I could make it act near enough like one of the "proper" IOC containers out there, but I just know that, that tinkering will never happen, as I am always eager to move on to something new. So yeah just stick to using one of the big "proper" IOC containers out there.
What Does It Do
In this section I will should you how to use BarbarianIOC
, and
what that looks like in a typical application
How Do You Configure The Container
The container can be configured using a fluent like interface something like this:
Container = new Container();
int someMockAge = 23; // this could come from anywhere (App.Config / database etc etc)
//Register ALL components
container.RegisterComponents(
//where you can use concrete type
new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
//or you can use an interface and it's implementation
new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
//and you can also declare singleton instance mode if you like
new Component().For<SomeIBazDependantClass>().WithInstanceMode(InstanceMode.Singleton),
//and even supply some non IOC provided constructor params by way of an anonymous object
new Component().For<SomeFooDependantClass>()
.DependsOn(new
{
age=someMockAge
})
.WithInstanceMode(InstanceMode.Transient)
);
//allow the container to wire stuff up (essentially create Expression.New for all
//components to allow Container to compile and create some quicker lookup delegates)
container.WireUp();
Allow For Non IOC Constructor Parameters
One of the important issues I wanted to tackle was to not only allow container generated constructor parameters to be provided, but also user specified constructor parameters. I decided to do this using a simple anonymous object, where you just specify the name of the constructor parameter and its value. An example of this is shown below, where we can provide these extra non IOC constructor parameters when we configure the IOC container as follows:
container.RegisterComponents(
new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
new Component().For<SomeFooDependantClass>()
.DependsOn(new
{
age=someMockAge
})
.WithInstanceMode(InstanceMode.Transient)
);
Note the use of the DependsOn(..)
method and the anonymous object
which provides the age
NON IOC created constructor parameter, which
may be needed by some class as shown below:
public class SomeFooDependantClass
{
/// <summary>
/// Note : That the NON IOC container registered constructor parameters can appear where ever, they do not need to appear at
/// the end/start. As BarbarianIOC will work out which positions each of the parameters should be when it is time to resolve the
/// Type from the BarbarianIOC.Container
/// </summary>
[DependencyConstructorAttribute]
public SomeFooDependantClass(Foo foo, int age, IBaz baz)
{
}
}
This shows that we have a class that expects an age
constructor parameter
value, along with some other constructor parameters that SHOULD come from the
IOC container. Note the order of the non IOC generated constructor parameter is
in the middle of the constructor arguments, it can be anywhere, full mix and
match of IOC/non IOC created constructor parameters is supported. I was pleased
with that feature.
Constructor Injection
The IOC container that goes with this article allows for constructor injection, thus it is able to satisfy the following constructor
public class SomeIBazDependantClass
{
private IBaz somePropBaz;
[DependencyConstructorAttribute]
public SomeIBazDependantClass(IBaz baz)
{
}
}
As previously stated it is also possible to include extra NON IOC constructor params such as the age
parameter we just saw. Here is an example of how you would annotate your class
to tell it which constructor it was going to try and satisfy dependencies for.
public class SomeFooDependantClass
{
/// <summary>
/// Note : That the NON IOC container registered constructor parameters can appear where ever, they do not need to appear at
/// the end/start. As BarbarianIOC will work out which positions each of the parameters should be when it is time to resolve the
/// Type from the BarbarianIOC.Container
/// </summary>
[DependencyConstructorAttribute]
public SomeFooDependantClass(Foo foo, int age, IBaz baz)
{
}
}
It can be seen from the code above, that I have chosen to use a specialized
attribute called DependencyConstructorAttribute
which you WILL need
to use, to mark up your chosen constructor with. This will indicate to the IOC
container presented with this article, which constructor should be used to create
an object from the container.
Now there may be some amongst you, who see this as a violation of Separation
Of Concerns (SOC), and to be honest it is
slightly, as our objects now contain IOC specific data in them, by way of these
special DependencyConstructorAttributes
. However without this
DependencyConstructorAttribute
, which ConstructorInfo
to use
would be quite an error prone experience. Should the IOC container go for the
one with the most parameters / the least parameters / the one that has the most
IOC resolvable parameters. It's not an easy decision. I could have ade some
fancy fluent API registration code for it, but in the end I feel justified
in my use of a specialized attribute (DependencyConstructorAttribute
),
which the user can use to adorn their objects, which tells the IOC container,
hey you need to create an object using this constructor.
The order of the IOC / non IOC constructor parameters is NOT important, you can mix and match IOC / non IOC provided constructor parameters as much as you like . We will get on to how this works later.
Property Injection
The IOC container that goes with this article also allows for property injection, which can be configured as follows:
public class SomeIBazDependantClass
{
private IBaz somePropBaz;
/// <summary>
/// BarbarianIOC also allows for property injection (provided you use the
/// <see cref="BarbarianIOC.Attributes.DependencyAttribute">DependencyAttribute</see>)
/// </summary>
[Dependency]
public IBaz SomePropBaz
{
get
{
return somePropBaz;
}
set
{
somePropBaz = value;
}
}
}
As with the constructor injection, I opted for using a specialized attribute
DependencyAttribute
, which indicates which properties the IOC
container should try and inject.
Resolving Instances
When you need a object instance from the container all you need to do is
Resolve
it which is done as follows :
SomeIBazDependantClass x1 = container.Resolve<SomeIBazDependantClass>();
From there you should be able to use the object instance which will have had all its constructor/property IOC/Non IOC dependencies resolved automatically (providing you got the component registration configuration correct).
Here is an example of an object that was resolved from the container code attached to this article, you can clearly see it has all the properties/constructor vaues provided.
How Does It Work
Ok so now I have shown you how to use it, but I bet you want to know how it works. Well lucky for you, in this section I will talk you through the code and you can get to see the nitty gritty inner workings. Hopefully that will be interesting.
Registering Instances
Registering components (whether they be service instances or concrete types)
is all done using a fluent interface which starts with the Component
builder, which is used to create a ComponentRegistration
instance. The following method(s) are available to aid in the building of a
ComponentRegistration
entry:
Component For<TCOMP>()
: used for registering a concrete type, and is responsible for creating a instanceComponent ServiceFor<IInt, TCOMP>()
: used for registering a service which implements a certain interfaceComponent DependsOn<T>(T dependencies)
: used for supplying non IOC container satisfied constructor parameter(s)ComponentRegistration WithInstanceMode(InstanceMode instanceMode)
: which MUST be the final method called in theComponent
builder fluent API process and it responsible for creating the finalComponentRegistration
instance.
So with that in mind, lets see how the IOC container uses these
ComponentRegistration
objects. This is all down to 1 simple method called
RegisterComponents
which looks like this, which simply takes the
registrations and adds them to a Container
held collection
public void RegisterComponents(params ComponentRegistration[] registrations)
{
lock (syncLock)
{
foreach (ComponentRegistration componentRegistration in registrations.ToList())
{
components.Add(componentRegistration, null);
}
}
}
So now that we know we are striving to create ComponentRegistration
objects which are passed to the Container
, but what do these
ComponentRegistration
objects look like, well they look like this:
public class ComponentRegistration
{
public Type TypeToLookFor { get; private set; }
public Type TypeToCreate { get; private set; }
public InstanceMode InstanceMode { get; set; }
public bool HasManualConstructorParameters { get; set; }
public List<ConstructorParameterDependency> DependsOnValues { get; set; }
public ComponentRegistration(Type typeToCreate) : this(typeToCreate, typeToCreate)
{
}
public ComponentRegistration(Type typeToLookFor, Type typeToCreate)
{
TypeToLookFor = typeToLookFor;
TypeToCreate = typeToCreate;
DependsOnValues = new List<ConstructorParameterDependency>();
}
}
Let's now
continue to look at the specific Component
builder methods.
Registering Concrete Instances
This is how you would typically register a concrete type using the Component
builder methods
new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient)
So now let's look at how this works, this is pretty easy and is responsible
for creating a instance of a
ComponentRegistration
that will be returned at the end of the
Component
fluent API, so where you see the "return this
"
within the next couple of methods, that is the fluent API in actio
public Component For<TCOMP>()
{
componentRegistration = new ComponentRegistration(typeof(TCOMP));
return this;
}
Registering Service Instances
This is how you would typically register a interface and implementation type using the Component
builder methods
new Component().ServiceFor<IBaz,Baz>().WithInstanceMode(InstanceMode.Transient)
So now let's look at how this works, this is pretty easy and is responsible
for creating a instance of a
ComponentRegistration
that will be returned at the end of the
Component
fluent API.
public Component ServiceFor<TInt, TCOMP>()
{
componentRegistration = new ComponentRegistration(typeof(TInt), typeof(TCOMP));
return this;
}
Specifying The Instance Mode
The ComponentRegistration WithInstanceMode(InstanceMode instanceMode)
is the simplest of all the Component
builder methods, and simply
sets the InstanceMode
on the ComponentRegistration
that it is
currently building, and then returns the current ComponentRegistration
being built.
Here is ALL the code for the ComponentRegistration
WithInstanceMode(InstanceMode instanceMode) Component
builder method
public ComponentRegistration WithInstanceMode(InstanceMode instanceMode)
{
if (componentRegistration == null)
{
throw new ContainerConfigurationException("Configuration error WithInstanceMode<> MUST be last");
}
else
{
componentRegistration.InstanceMode = instanceMode;
return componentRegistration;
}
}
Providing Extra NON IOC Constructor Parameters
One of the things that I really wanted to achieve was the ability to satisfy constructor parameters that were not meant to come from the IOC container. In essence what I wanted was the ability to mix and match IOC container satisfied constructor parameter(s) with non IOC container satisfied constructor parameter(s). Where non IOC container satisfied constructor parameter(s) may be things like
- App settings
- Connections strings
- Static values
As before we use the Component
fluent interface to build up a
ComponentRegistration
object. The relevant Component
builder method for adding non IOC container satisfied constructor parameter(s)
is the DependsOn( )
method that is shown below.
public Component DependsOn<T>(T dependencies)
{
if (componentRegistration == null)
{
throw new ContainerConfigurationException(
"Configuration error DependsOn<> MUST be called after For<> or ImplementedBy<>");
}
else
{
List<ConstructorParameterDependency> constructorDependencies =
new List<ConstructorParameterDependency>();
foreach (string name in typeof(T).GetConstructors()[0].GetParameters()
.Select(p => p.Name))
{
PropertyInfo property = typeof(T).GetProperty(name);
ParameterExpression param = Expression.Parameter(typeof(T), "x");
Expression propertyAccess = Expression.Property(param, property);
Expression convert = Expression.Convert(propertyAccess, typeof(object));
Func<T, object> lambda =
Expression.Lambda<Func<T, object>>(convert, param).Compile();
var result = lambda(dependencies);
constructorDependencies.Add(new ConstructorParameterDependency(
property.PropertyType, name, Expression.Constant(result)));
}
if (constructorDependencies.Any())
{
componentRegistration.HasManualConstructorParameters = true;
componentRegistration.DependsOnValues = constructorDependencies;
}
else
{
componentRegistration.HasManualConstructorParameters = false;
}
return this;
}
}
The basic idea here is that we examine an anonymous object that was passed
into the DependsOn( )
method, and we build up a List<ConstructorParameterDependency>
values for each of the properties/property values found on the anonymous object
that was passed into the DependsOn( )
method. These are later
examined by the IOC container to work out which constructor parameter values
should come from where. Essentially the IOC container orders the
ConstructorParameterDependency
based on their Position
property.
We could have used some reflection here instead, as these constructor
parameter values are essentially cached once the 1st instance of a certain type
has been requested from the IOC container, so reflection may have been a better
choice here instead of using Expression
trees, but since everything else is
Expression
based I stuck with it. The performance difference in this case would
be minute, so I stuck to using the Expression
APIs.
We have already seen an example of the usage of this, but just for completeness here it is again:
//Register ALL components
container.RegisterComponents(
//and even supply some non IOC provided constructor params by way of an anonymous object
new Component().For<SomeFooDependantClass>()
.DependsOn(new
{
age = someMockAge
})
.WithInstanceMode(InstanceMode.Transient)
);
How The Instance Mode Works
At present the IOC container associated with this article only supports 2 instance modes:
- Singleton : Every instance of the object resolved with this instance mode is expected to be the exact same instance
- Transient : Every instance of the object resolved with this instance mode is expected to be a new unique instance
Transient Instance Mode
The transient mode uses a simple factory which uses a delegate
that the IOC container has cached. Where the
delegate
(Expression.New
is compiled into a delegate
)
is invoked, to return a new object instance. The TransientFactory
can be seen
below:
/// <summary>
/// Transient object factory which returns a new instance
/// when Create() method is called
/// </summary>
public class TransientFactory : IFactoryProvider
{
private Delegate objectCreator;
public TransientFactory(Delegate objectCreator)
{
this.objectCreator = objectCreator;
}
public object Create()
{
return objectCreator.DynamicInvoke();
}
}
Singleton Instance Mode
The singleton mode also uses a simple factory which essentially uses a delegate
that the IOC container has cached. Where the
delegate
(Expression.New
is compiled into a delegate
)
is used as the value of a
Lazy<T>
(which is
really a cheeky way of creating a thread safe singleton), where the new object
instance returned by invoking the delegate used as the Lazy<T>.Value
. The
SingletonFactory
can be seen
below:
/// <summary>
/// Singleton object factory which returns the same singleton instance
/// when Create() method is called
/// </summary>
public class SingletonFactory : IFactoryProvider
{
private Lazy<object> singletonCreator;
public SingletonFactory(Delegate objectCreator)
{
singletonCreator = new Lazy<object>(() => objectCreator.DynamicInvoke());
}
public object Create()
{
return singletonCreator.Value;
}
}
Putting This To The Test
Based on what we just talked about above, lets assume we have the following IOC container configuration
Container container = new Container();
int someMockAge = 23; // this could come from anywhere (App.Config / database etc etc)
//Register ALL components
container.RegisterComponents(
//where you can use concrete type
new Component().For<Foo>().WithInstanceMode(InstanceMode.Transient),
//or you can use an interface and it's implementation
new Component().For<IBaz>().ImplementedBy<Baz>().WithInstanceMode(InstanceMode.Transient),
//and you can also declare singleton instance mode if you like
new Component().For<SomeIBazDependantClass>().WithInstanceMode(InstanceMode.Singleton),
//and even supply some non IOC provided constructor params by way of an anonymous object
new Component().For<SomeFooDependantClass>()
.DependsOn(new
{
age=someMockAge
})
.WithInstanceMode(InstanceMode.Transient)
);
Which when we examine the simple tests as following we get the results we want/expect based on the configuration above (Click below for bigger image)

Wiring It All Together
Once you have registered all your Component
(s) you need to tell
the container to wire everything together. What this does in a nutshell is to
create a Expression.Constant
delegate for each of the constructor parameters making sure to adhere to the
InstanceMode
that was requested, and to also create a final
delegate that is responsible for newing up the registering type, that will be
used by the Resolve<T>
method.
It does this recursively until all
the constructor dependencies of all the register components have matching
delegates to create their constructor parameters, and that there is a final
delegate that is responsible for newing up the registering type again taking
into account the InstanceMode
that was requested.
This is only done once per type. What we then end up with, is a bunch of
constructor parameter value creation delegates (factories essentially) that we
use to create List<ConstructorParameterDependency>
(the same as the
Non IOC generated constructor parameters) for each of the IOC required
constructor parameters. This is then merged with any
ConstructorParameterDependency
instances that were created by using the
DependsOn(..)
method.
The final step is to just sort the List<ConstructorParameterDependency>
using their Position
property, and to use an Expression.New
inside a
Expression.Lambda
to create a factory for creating the instance that is being
examined. This will also take into account the InstanceMode
.
The most relevant methods of the Container
are shown below:
public void WireUp()
{
foreach (ComponentRegistration key in components.Where(c => c.Value == null).Select(c => c.Key).ToList())
{
CreateFactory(key, GetConstructorDelegateForType(key.TypeToCreate), key.InstanceMode);
}
}
private Delegate GetConstructorDelegateForType(Type type)
{
ComponentRegistration componentRegistration = null;
//look for constructor that is marked with DependencyConstructorAttribute,
//if there is none just try and go for the default constructor
ConstructorInfo ctor = type.GetConstructors()
.Where(x => x.GetCustomAttributes(typeof(DependencyConstructorAttribute), false).Count() > 0).SingleOrDefault();
if (ctor == null)
{
ctor = type.GetConstructors()[0];
}
foreach (var ctorArg in ctor.GetParameters())
{
bool isParamCoveredByManualRegistration = IsParamCoveredByManualRegistration(type, ctorArg);
if (!isParamCoveredByManualRegistration)
{
bool parameterKeyFound = components.Keys.Any(x => x.TypeToCreate == ctorArg.ParameterType ||
ctorArg.ParameterType.IsAssignableFrom(x.TypeToLookFor));
if (!parameterKeyFound)
{
throw new ContainerConfigurationException(string.Format("Couldn't find ctor argument {0}", ctorArg.GetType()));
}
else
{
componentRegistration = FetchComponentRegistration(ctorArg.ParameterType);
if (components[componentRegistration] == null)
{
Delegate delegateForType = GetConstructorDelegateForType(componentRegistration.TypeToCreate);
CreateFactory(componentRegistration, delegateForType, componentRegistration.InstanceMode);
}
}
}
}
List<ConstructorParameterDependency> args = new List<ConstructorParameterDependency>();
foreach (var ctorArg in ctor.GetParameters())
{
bool isParamCoveredByManualRegistration = IsParamCoveredByManualRegistration(type, ctorArg);
if (!isParamCoveredByManualRegistration)
{
componentRegistration = FetchComponentRegistration(ctorArg.ParameterType);
args.Add(new ConstructorParameterDependency(
ctorArg.ParameterType,
ctorArg.Name,
Expression.Constant(components[componentRegistration].Create()),
ctorArg.Position));
}
}
componentRegistration = FetchComponentRegistration(type);
if (componentRegistration != null)
{
if (componentRegistration.DependsOnValues.Any())
{
args.AddRange(componentRegistration.DependsOnValues);
}
}
return Expression.Lambda(Expression.New(ctor, args.OrderBy(x => x.Position)
.Select(x => x.Value).ToArray())).Compile();
}
private bool IsParamCoveredByManualRegistration(Type constructorOwnerType, ParameterInfo constructorArg)
{
ComponentRegistration componentRegistration = FetchComponentRegistration(constructorOwnerType);
if (!componentRegistration.HasManualConstructorParameters)
{
return false;
}
else
{
ConstructorParameterDependency constructorParameterDependency =
componentRegistration.DependsOnValues.SingleOrDefault(
x => x.ArgType == constructorArg.ParameterType && x.Name == constructorArg.Name);
if (constructorParameterDependency != null)
{
constructorParameterDependency.Position = constructorArg.Position;
return true;
}
else
{
return false;
}
}
}
private void CreateFactory(ComponentRegistration key, Delegate @delegate, InstanceMode instanceMode)
{
IFactoryProvider factoryProvider = null;
if (instanceMode == InstanceMode.Transient)
{
factoryProvider = new TransientFactory(@delegate);
}
if (instanceMode == InstanceMode.Singleton)
{
factoryProvider = new SingletonFactory(@delegate);
}
lock (syncLock)
{
components[key] = factoryProvider;
}
}
How Resolving Instance Works
The resolving of instances is probably the simplest part, as all the hard
work should have already been done by the WireUp()
method, who's job
it is to ensure that there is a IFactoryProvider
created for each
registered Component
. So resolving an object from the container
really just boils down to these 3 simple steps
- Find the
ComponentRegistration
that has the correctTypeToCreate
as the requested generic type to theResolve<T>( )
method - Use the
IFactoryProvider
to create an instance of the correct type (where theIFactoryProvider
would have already been created by theWireUp()
method), or throw anException
if we can't find aIFactoryProvider
. If we get to the point where we chuck anException
here, this may be due to a missing or badComponentRegistration
. - If we get a new object created by the
IFactoryProvider
, simply go through its properties that are marked up with the[Dependency]
attribute and satisfy them from the IOC container. Once this is done simply return the object which should have now had the following done to it:- IOC generated constructor parameters should have been satisfied
- Non IOC constructor parameters should have been satisfied
- Properties that are marked as IOC dependencies (ones with
[Dependency]
attribute) should also have been satisfied
This can be seen within the 2 methods shown below:
public T Resolve<T>()
{
lock (syncLock)
{
IFactoryProvider creator = components.Where(x => x.Key.TypeToCreate == typeof(T)).Select(x => x.Value).SingleOrDefault();
if (creator != null)
{
T newlyCreatedObject = (T)creator.Create();
SatisfyProperties<T>(newlyCreatedObject);
return newlyCreatedObject;
}
else
{
throw new ContainerConfigurationException(string.Format(
"Couldn't create instance of {0} could not find correct IFactoryProvider. This may be down to missing Component registration",
typeof(T).FullName));
}
}
}
private void SatisfyProperties<T>(T newlyCreatedObject)
{
foreach (PropertyInfo prop in newlyCreatedObject.GetType().GetProperties()
.Where(x => x.GetCustomAttributes(typeof(DependencyAttribute), false).Count() > 0))
{
IFactoryProvider factoryProvider = components.Single(x => x.Key.TypeToCreate == prop.PropertyType ||
prop.PropertyType.IsAssignableFrom(x.Key.TypeToLookFor)).Value;
if (factoryProvider != null)
{
prop.SetValue(newlyCreatedObject, factoryProvider.Create(), null);
}
else
{
throw new ContainerConfigurationException(string.Format(
"Couldn't find instance of {0} to use for property injection", prop.PropertyType.FullName));
}
}
}
That's It
Anyway that's it. I kind of wrote this one just for fun really. As I say I would really not recommend anyone use this, as there are some seriously good IOC containers out there, and I would stick to using one of them. This one is a mere play thing to gain some insight as to how one might go about building a simple IOC container, where you may not be able to use 3rd party Dlls, or just want something dead simple.
Anyway like I say I just wrote this one for fun really, and if you feel like leaving a comment / vote that would be most welcome.