Skip to content

Instantly share code, notes, and snippets.

@half-ogre
Created February 9, 2013 17:46
Show Gist options
  • Save half-ogre/4746279 to your computer and use it in GitHub Desktop.
Save half-ogre/4746279 to your computer and use it in GitHub Desktop.
A simple object creator to use as an IoC container
using System;
using System.Collections.Generic;
using System.Linq;
namespace RpgRooms.Website
{
public class ObjectCreator : IObjectRegistry
{
static ObjectCreator()
{
Current = new ObjectCreator();
}
static public ObjectCreator Current { get; internal set; }
readonly Dictionary<Type, List<Func<object[], object>>> creatorBindings = new Dictionary<Type, List<Func<object[], object>>>();
void AddCreator(
Type type,
Func<object[], object> creator)
{
if (creatorBindings.ContainsKey(type))
creatorBindings[type].Add(creator);
else
creatorBindings.Add(type, new List<Func<object[], object>> { creator });
}
object Create(
Type type,
params object[] constructorArguments)
{
var constructors = type.GetConstructors();
if (constructors.Length != 1)
throw new InvalidOperationException(string.Format(
"Cannot create type '{0}' because it has more than one constructor.",
type.Name));
var ctorArgs = new List<object>();
foreach (var ctorParamInfo in constructors[0].GetParameters())
{
object ctorArg = null;
var possibleCtorArgs = constructorArguments.Where(argument => ctorParamInfo.ParameterType.IsInstanceOfType(argument)).ToArray();
if (possibleCtorArgs.Length > 1)
throw new InvalidOperationException(string.Format(
"Cannot create type '{0}' because multiple arguments were provided that could be assigned to the '{1}' constructor parameter.",
type.Name,
ctorParamInfo.Name));
if (possibleCtorArgs.Length == 1)
ctorArg = possibleCtorArgs[0];
if (ctorArg == null && IsTypeBound(ctorParamInfo.ParameterType))
ctorArg = Build(ctorParamInfo.ParameterType);
if (ctorArg == null)
throw new InvalidOperationException(string.Format(
"Cannot create type '{0}' because no argument was provided for the constructor parameter '{1}' and no creator is bound for type '{2}'.",
type.Name,
ctorParamInfo.Name,
ctorParamInfo.ParameterType.Name));
ctorArgs.Add(ctorArg);
}
if (ctorArgs.Count > 0)
return Activator.CreateInstance(type, ctorArgs.ToArray());
return Activator.CreateInstance(type);
}
public IEnumerable<object> BuildAll(
Type type,
params object[] constructorArguments)
{
List<Func<object[], object>> instanceCreators;
var serviceTypeIsRegistered = creatorBindings.TryGetValue(type, out instanceCreators);
if (!serviceTypeIsRegistered)
{
if (type.IsClass && !type.IsAbstract)
{
Bind(type, type, true);
return BuildAll(type, constructorArguments);
}
throw new InvalidOperationException(string.Format(
"Cannot create type '{0}' because no creator is bound for it.",
type.Name));
}
return instanceCreators
.Select(instanceCreator => instanceCreator(constructorArguments));
}
public IEnumerable<T> BuildAll<T>(params object[] constructorArguments)
{
var type = typeof(T);
return BuildAll(
type,
constructorArguments)
.Select(instance => (T)instance);
}
public object Build(
Type type,
params object[] constructorArguments)
{
return BuildAll(
type,
constructorArguments)
.SingleOrDefault();
}
public T Build<T>(params object[] arguments)
{
var type = typeof(T);
return (T)Build(
type,
arguments);
}
public bool IsTypeBound(Type type)
{
return creatorBindings.Keys.Any(key => key == type);
}
void Bind(
Type forType,
Type toType,
bool allowConcreteToType)
{
if (!forType.IsInterface && !forType.IsAbstract && !allowConcreteToType)
throw new InvalidOperationException(string.Format(
"Cannot register for type '{0}' because it is not an interface or abstract class.",
forType.Name));
if (!forType.IsAssignableFrom(toType))
throw new InvalidOperationException(string.Format(
"Cannot register for type '{0}' because it is not assignable from type '{1}'.",
forType.Name,
toType.Name));
AddCreator(
forType,
arguments => Create(toType, arguments));
}
public void Bind(
Type forType,
Type toType)
{
Bind(forType, toType, false);
}
public void Bind<TFor, TTo>()
{
Bind(
typeof(TFor),
typeof(TTo));
}
public void Bind(
Type type,
object instance)
{
AddCreator(
type,
arguments => instance);
}
public void Bind<T>(T instance)
{
Bind(typeof(T), instance);
}
public void Bind<T>(Func<T> creator)
{
var type = typeof(T);
AddCreator(
type,
arguments => creator());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment