Introduction
This article will guide you through creating a simple Dependency Injector
Background
I wanted to create a language that was like javascript but without the little annoyances that bothered me. I wanted to have multiple inheritence, test, error handling and a REPL. In order to do this i had to create a many things my self as i did not want to use any existing libraries so that i could have a better understanding of how it worked. so first thing i had to build was a Depdendance Injector so here it is.
What Is It
What is a Depdence Injector
well a depdence injector allows you to ask for an interface or abstract class and be given back an implementation of that interface or abstract without knowing any details about that class. in fact using a dependance injector you can even write that class in a different library which is not referenced by the class using it.
But why would i do that, or want to do that
Because it means that you can completely decouple your code from all but the interfaces. which in turn would allow you enchance code in different library, meaning once code is compiled into a library it never needs to be edited again and you can still enhance you application.
it also means you can easily test an object independant of all other objects by using Mocks and telling the injector to return a mock.
It Keeps all your code clean and easy to maintain by replacement.
How Does it Work
It functions like a dictionary or a look up, you ask for something by passing a type and it returns something that can be cast as that type. most of the time it would be an interface you are asking for.
so Lets say you have a type IPerson which has a string property Name
the client code doesnt need to know anything about how it implements Name it just needs to know that it does.
this allows you to obey the Dictum "Always Code to Interface" Literally.
it also means that you can create a dummy to test your code whilst someone else is writting a propper implementation of IPerson.
so each object have in its constructor a paramater that is the interface of the injector that is used in the client app
i.e.
public interface IPerson {string Name {get;set;}}
and the a concrete version of that Interface / Abstract would look like
public internal class DummyPerson : IPerson
{
private IInjector Injector {get; set;}
public DummyPerson(IInjector injector)
{
Injector = injector;
}
public string Name {get;set;}
}
and somewhere in the code you would need to register through the IInjector interface
Injector.Add(typeof(IPerson), typeof(DummyPerson));
and then when someone else where to ask for an IPerson they would get and instance of DummyPerson
i.e.
IPerson thisGal = Injector.Get(typeof(IPerson))
which would look up in the dictionary for the type registered against the type IPerson which in this case would be DummyPerson. but the constructor for Dummy Person is not empty it has Parameters, so what happens is that the Injector uses reflection to inspect those parameters and find the concrete versions of each parameter type.
var Params = this.Type.GetConstructors()[0].GetParameters();
var Instances = Params.Select(p => GetInstance(p.ParameterType, passedParameters)).ToArray();
if (<span style="font-size: 9pt;">constructorParameters.Length > 0 )
{
</span>
Which relies on the Function GetInstance Below, which makes the whole operation Recursive as for each parameter it looks up the type and asks the injector to determine the concrete class, inspects its first constructor and then does the same for all that functions parameters
private object GetInstance(Type t, IEnumerable<object> parms)
{
var enumerable = parms as object[] ?? parms.ToArray();
if (enumerable.Length < 0)
{
var passedObject = enumerable.FirstOrDefault(p => p.GetType() == t);
if (passedObject == null)
{
return Injector.Get(t, enumerable);
}
else
{
return passedObject;
}
}
else
{
return Injector.Get(t, enumerable);
}
}
Code
What i came up with is below - it is far from perfect but it works
internal class InternalInjector
{
private Dictionary<Type, Record> _dict = new Dictionary<Type, Record>();
public T Get<T>(IEnumerable<object> parms)
{
return (T)Get(typeof(T), parms);
}
public object Get(Type t, IEnumerable<object> parms)
{
Record rtn;
if (!_dict.TryGetValue(t, out rtn)) throw new TypeInitializationException("Failes to Create Type " + t.ToString() + " as there is No Registered Concrete for that Abstaract", new Exception("Record Not Found"));
return rtn.GetInstance(parms);
}
public object Get(Type t) { return _dict[t].Instance; }
private Record Find(Type t)
{
Record rtn = null;
if (!_dict.TryGetValue(t, out rtn))
{
rtn = new Record(this);
_dict.Add(t, rtn);
}
return rtn;
}
public T Get<T>()
{
return (T)_dict[typeof(T)].Instance;
}
public void Add(Type interf, Type conc)
{
var rtn = Find(interf);
rtn.Type = conc;
rtn.LifeCycle = Record.LifeCycleEnumeration.InstancePerCall;
}
public void AddMethod(Type interf, Type conc, MethodInfo method)
{
var rtn = Find(interf);
rtn.Type = conc;
rtn.LifeCycle = Record.LifeCycleEnumeration.InstancePerCall;
rtn.Method = method;
}
public void AddInstance(Type interf, Type conc, object instnc)
{
var rtn = Find(interf);
rtn.Type = conc;
rtn.LifeCycle = Record.LifeCycleEnumeration.Singleton;
rtn.Instance = instnc;
}
public void Add(Type interf, Type conc, Record.LifeCycleEnumeration lc)
{
var rtn = Find(interf);
rtn.Type = conc;
rtn.LifeCycle = lc;
}
public void Add<TI, TC>()
{
var rtn = Find(typeof(TI));
rtn.Type = typeof(TC);
rtn.LifeCycle = Record.LifeCycleEnumeration.InstancePerCall;
}
public void Add<TI, TC>(MethodInfo method)
{
var rtn = Find(typeof(TI));
rtn.Type = typeof(TC);
rtn.LifeCycle = Record.LifeCycleEnumeration.InstancePerCall;
rtn.Method = method;
}
public void Add<TI, TC>(Record.LifeCycleEnumeration lifeCycle)
{
var rtn = Find(typeof(TI));
rtn.Type = typeof(TC);
rtn.LifeCycle = lifeCycle;
}
public void Add<TI, TC>(MethodInfo method, Record.LifeCycleEnumeration lifeCycle)
{
var rtn = Find(typeof(TI));
rtn.Type = typeof(TC);
rtn.LifeCycle = lifeCycle;
rtn.Method = method;
}
private object ReturnAndRemove(object itm, List<object> items)
{
items.Remove(itm);
return itm;
}
internal class Record
{
private InternalInjector Injector { get; set; }
public MethodInfo Method
{
get { return _method; }
set
{
_method = value;
UsesConstructor = (Method == null);
}
}
private bool UsesConstructor = true;
public enum LifeCycleEnumeration
{
Singleton,
InstancePerCall
}
public LifeCycleEnumeration LifeCycle { get; set; }
public Type Type { get; set; }
private object _instance = null;
private MethodInfo _method;
public Record(InternalInjector injector)
{
Injector = injector;
}
public object CreateInstance(IEnumerable<object> parms)
{
var passedParameters = parms.ToArray();
if (UsesConstructor)
{
var constructorParameters = this.Type.GetConstructors()[0].GetParameters().Select(p => GetInstance(p.ParameterType, passedParameters)).ToArray();
return constructorParameters.Length > 0 ? Activator.CreateInstance(Type, constructorParameters) : Activator.CreateInstance(Type);
}
else
{
var methodParameters = this.Method.GetParameters().Select(p => GetInstance(p.ParameterType, passedParameters)).ToArray();
return Method.Invoke(this, methodParameters);
}
}
private object GetInstance(Type t, IEnumerable<object> parms)
{
var enumerable = parms as object[] ?? parms.ToArray();
if (enumerable.Length > 0)
{
var passedObject = enumerable.FirstOrDefault(p => p.GetType() == t);
if (passedObject == null)
{
return Injector.Get(t, enumerable);
}
else
{
return passedObject;
}
}
else
{
return Injector.Get(t, enumerable);
}
}
public object CreateInstance()
{
return CreateInstance(new object[] { });
}
public object GetInstance(IEnumerable<object> parms)
{
return LifeCycle == LifeCycleEnumeration.InstancePerCall ? CreateInstance(parms) : _instance ?? (_instance = CreateInstance(parms));
}
public object Instance
{
get { return LifeCycle == LifeCycleEnumeration.InstancePerCall ? CreateInstance() : _instance ?? (_instance = CreateInstance()); }
set { _instance = value; }
}
}
}
Points of Interest
This Actual Code is used as part of a project Called "Baik Programming Language at Code Plex
- https://baik.codeplex.com/
In a Future Article i will write how to write your own scipring language from scratch and include a reference to this article.
History
- Initial Version Published 2016-01-06