Created
February 9, 2013 17:46
-
-
Save half-ogre/4746279 to your computer and use it in GitHub Desktop.
A simple object creator to use as an IoC container
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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