Skip to content

Instantly share code, notes, and snippets.

@Dalstroem
Last active November 16, 2016 11:34
Show Gist options
  • Save Dalstroem/273ce0f3804ccd725e3abc6507e0d44e to your computer and use it in GitHub Desktop.
Save Dalstroem/273ce0f3804ccd725e3abc6507e0d44e to your computer and use it in GitHub Desktop.
Simple IoC container with dependency injection feature.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Dalstroem
{
public class Container
{
private readonly ConcurrentDictionary<Type, ContainerEntry> _entries = new ConcurrentDictionary<Type, ContainerEntry>();
/// <summary>
/// Registers a pre-constructed instance against a type. This is useful
/// when an object requires a specific state.
/// </summary>
/// <param name="service"></param>
/// <param name="implementation"></param>
public void RegisterInstance(Type service, object implementation)
{
if (implementation == null) throw new ArgumentNullException(nameof(implementation), @"The parameter cannot be null.");
RegisterHandler(service, (container, objects) => implementation);
}
/// <summary>
/// Registers an implementation against a type. A new instance of the
/// type is returned every time.
/// </summary>
/// <param name="implementation"></param>
public void RegisterPerRequest(Type implementation)
{
RegisterPerRequest(implementation, implementation);
}
/// <summary>
/// Registers an implementation against a type. A new instance of the
/// type is returned every time.
/// </summary>
/// <param name="service"></param>
/// <param name="implementation"></param>
public void RegisterPerRequest(Type service, Type implementation)
{
if (implementation == null) throw new ArgumentNullException(nameof(implementation), @"The parameter cannot be null.");
RegisterHandler(service, (container, objects) => container.BuildInstance(implementation, objects));
}
/// <summary>
/// Registers an implementation against a type. The same instance of
/// the type is returned every time. The object is constructed when
/// first requested.
/// </summary>
/// <param name="service"></param>
/// <param name="implementation"></param>
public void RegisterSingleton(Type service, Type implementation)
{
if (implementation == null) throw new ArgumentNullException(nameof(implementation), @"The parameter cannot be null.");
object singleton = null;
RegisterHandler(service, (container, objects) => singleton ?? (singleton = container.BuildInstance(implementation, objects)));
}
/// <summary>
/// Registers a handler against a type. This allows the factory method
/// to take advantage of the container itself which is useful in
/// complex construction scenarios.
/// </summary>
/// <param name="service"></param>
/// <param name="handler"></param>
public void RegisterHandler(Type service, Func<Container, object[], object> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler), @"The parameter cannot be null.");
var entry = _entries.GetOrAdd(service, type => new ContainerEntry());
entry.Handler = handler;
}
/// <summary>
/// Unregisters a service.
/// </summary>
/// <param name="service"></param>
public void Unregister(Type service)
{
if (service == null) throw new ArgumentNullException(nameof(service), @"The parameter cannot be null.");
ContainerEntry entry;
_entries.TryRemove(service, out entry);
}
/// <summary>
/// Gets an instance of a registered type.
/// </summary>
/// <param name="service"></param>
/// <param name="optional"></param>
/// <returns></returns>
public object GetInstance(Type service, params object[] optional)
{
ContainerEntry entry;
if (_entries.TryGetValue(service, out entry))
{
return entry.Handler(this, optional);
}
return null;
}
/// <summary>
/// Builds an instance of the type provided.
/// </summary>
/// <param name="type"></param>
/// <param name="optional"></param>
/// <returns></returns>
private object BuildInstance(Type type, object[] optional)
{
var args = DetermineConstructorArgs(type, optional);
return ActivateInstance(type, args);
}
/// <summary>
/// Returns an array of parameters of the type provided.
/// </summary>
/// <param name="type"></param>
/// <param name="optional"></param>
/// <returns></returns>
private object[] DetermineConstructorArgs(Type type, object[] optional)
{
var constructors = from c in type.GetConstructors()
let parameters = c.GetParameters()
where c.IsPublic
orderby parameters.Length descending
select c;
var constructor = constructors.FirstOrDefault();
var args = new List<object>();
if (constructor != null)
{
var startIndex = 0;
foreach (var parameter in constructor.GetParameters())
{
var parameterType = parameter.ParameterType;
var instance = GetInstance(parameterType);
var index = Array.FindIndex(optional, startIndex, o => parameterType.IsInstanceOfType(o));
if (instance == null && index > -1)
{
instance = optional[index];
startIndex = index + 1;
}
args.Add(instance);
}
}
return args.ToArray();
}
/// <summary>
/// Creates the instance of the type with the arguments.
/// </summary>
/// <param name="type"></param>
/// <param name="args"></param>
/// <returns></returns>
private object ActivateInstance(Type type, object[] args)
{
return Activator.CreateInstance(type, args);
}
}
internal class ContainerEntry
{
public Func<Container, object[], object> Handler { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment