internal struct CachedTypeInfo
{
public Type Type;
public bool IsSingleton;
}
internal class ConstructorCache
{
private static ConcurrentDictionary<Type, ConstructorInfo> _constructorCache = new ConcurrentDictionary<Type, ConstructorInfo>();
public static ConstructorInfo GetConstructor(Type type)
{
ConstructorInfo constructor;
if (!_constructorCache.TryGetValue(type, out constructor))
constructor = _constructorCache[type] = DiscoverConstructor(type.GetTypeInfo());
return constructor;
}
private static ConstructorInfo DiscoverConstructor(TypeInfo typeInfo)
{
var constructors = typeInfo.DeclaredConstructors;
if (constructors.Any())
return constructors.First();
return null;
}
}
public interface IContainer : IServiceProvider
{
void Register<TService, TImplementation>() where TImplementation : TService;
void Register<TService, TImplementation>(bool singleton) where TImplementation : TService;
void Register<TService>(Type implementation, bool singleton);
void Register<TService>(Type implementation, Action<TService> callback, bool singleton);
void Register(Type service, Type implementation, bool singleton);
void Register<TService>(TService instance);
T Resolve<T>();
bool IsRegistered<T>();
}
internal class ParameterCache
{
private static ConcurrentDictionary<ConstructorInfo, List<ParameterInfo>> _parameterCache = new ConcurrentDictionary<ConstructorInfo, List<ParameterInfo>>();
public static List<ParameterInfo> GetParameters(ConstructorInfo constructor)
{
List<ParameterInfo> parameterInfo;
if (!_parameterCache.TryGetValue(constructor, out parameterInfo))
{
// Not in cache, discover and add to cache.
parameterInfo = _parameterCache[constructor] = DiscoverParameters(constructor);
}
return parameterInfo;
}
private static List<ParameterInfo> DiscoverParameters(ConstructorInfo constructor)
{
return constructor.GetParameters().ToList();
}
}
public class SimpleContainer : IContainer
{
private readonly ConcurrentDictionary<Type, CachedTypeInfo> _serviceTypeLookup = new ConcurrentDictionary<Type, CachedTypeInfo>();
private readonly ConcurrentDictionary<Type, object> _serviceInstanceLookup = new ConcurrentDictionary<Type, object>();
private readonly ConcurrentDictionary<Type, Action<object>> _serviceTypeCallbackLookup = new ConcurrentDictionary<Type, Action<object>>();
#region IContainer Implementation
public void Register<TService, TImplementation>() where TImplementation : TService
{
_serviceTypeLookup[typeof(TService)] = new CachedTypeInfo { Type = typeof(TImplementation), IsSingleton = true };
}
public void Register<TService, TImplementation>(bool singleton = true) where TImplementation : TService
{
_serviceTypeLookup[typeof(TService)] = new CachedTypeInfo { Type = typeof(TImplementation), IsSingleton = singleton };
}
public void Register<TService>(Type implementationType, bool singleton = true)
{
if (implementationType == null)
throw new ArgumentNullException("implementationType cannot be null.");
_serviceTypeLookup[typeof(TService)] = new CachedTypeInfo { Type = implementationType, IsSingleton = singleton };
}
public void Register<TService>(Type implementationType, Action<TService> callback, bool singleton = true)
{
if (implementationType == null)
throw new ArgumentNullException("serviceType cannot be null.");
_serviceTypeLookup[typeof(TService)] = new CachedTypeInfo { Type = implementationType, IsSingleton = singleton };
if (callback != null)
_serviceTypeCallbackLookup[typeof(TService)] = (x) => callback((TService)x);
}
public void Register(Type serviceType, Type implementationType, bool singleton = true)
{
if (serviceType == null)
throw new ArgumentNullException("serviceType cannot be null.");
if (implementationType == null)
throw new ArgumentNullException("serviceType cannot be null.");
if (!serviceType.IsAssignableFrom(implementationType))
throw new ArgumentException(string.Format("Service could not be registered. {0} does not implement {1}.", implementationType.Name, serviceType.Name));
_serviceTypeLookup[serviceType] = new CachedTypeInfo { Type = implementationType, IsSingleton = singleton };
}
public void Register<TService>(TService instance)
{
if (instance == null)
throw new ArgumentNullException("instance cannot be null.");
_serviceInstanceLookup[typeof(TService)] = instance;
}
public T Resolve<T>()
{
return (T)Resolve(typeof(T));
}
private object Resolve(Type type)
{
CachedTypeInfo containerType;
object instance = null;
// If the type isn't registered, register the type to itself.
if (!_serviceTypeLookup.TryGetValue(type, out containerType))
{
Register(type, type);
containerType = new CachedTypeInfo { Type = type, IsSingleton = true };
}
// TODO: Should it use the instance by default? I'd assume so initially.
// Check if the service has an instance in the list of instances, if so, return it here.
if (_serviceInstanceLookup.TryGetValue(type, out instance))
return instance;
var constructor = ConstructorCache.GetConstructor(containerType.Type);
if (constructor != null)
{
// Get constructor parameters.
var parameters = ParameterCache.GetParameters(constructor);
var parameterObjects = new List<object>();
foreach (var parameter in parameters)
{
parameterObjects.Add(Resolve(parameter.ParameterType));
}
var obj = Activator.CreateInstance(containerType.Type, parameterObjects.ToArray());
Action<object> callback;
if (_serviceTypeCallbackLookup.TryGetValue(type, out callback))
callback(obj);
if (containerType.IsSingleton)
_serviceInstanceLookup[type] = obj;
return obj;
}
else
{
// Return null rather than throw an exception for resolve failures.
// This null will happen when there are 0 constructors for the supplied type.
return null;
}
}
public bool IsRegistered<TService>()
{
if (_serviceTypeLookup.ContainsKey(typeof(TService)) || _serviceInstanceLookup.ContainsKey(typeof(TService)))
return true;
return false;
}
#endregion
#region IServiceProvider Implementation
public object GetService(Type serviceType)
{
return Resolve(serviceType);
}
#endregion
}
internal static class TypeResolver
{
private static ConcurrentDictionary<Type, Type> _typeCache = new ConcurrentDictionary<Type, Type>();
public static Type Resolve<T>(string className)
{
var type = typeof(T);
Type implementationType = null;
if (!_typeCache.TryGetValue(type, out implementationType))
{
implementationType =
_typeCache[type] = Resolve(className, type);
}
return implementationType;
}
public static Type Resolve(string implementingType, Type serviceType = null)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var types = assemblies.SelectMany(a => a.GetTypes());
Type type = null;
if (serviceType != null)
type = types.FirstOrDefault(t => t.Name == implementingType && serviceType.IsAssignableFrom(t));
else
type = types.FirstOrDefault(t => t.Name == implementingType);
return type;
}
}