using System; using System.Collections.Generic; using System.Reflection; using System.Diagnostics; using Toolbox; namespace Configuration.IoC { sealed class ServiceLocator { [ThreadStatic] static ServiceLocator _instance; readonly Dictionary _info = new Dictionary(); readonly Dictionary _instances = new Dictionary(); public static IDisposable register(InterfaceT instance) where InterfaceT : class { return Instance.registerInternal(instance); } static ServiceLocator Instance { get { return _instance ?? (_instance = new ServiceLocator()); } } public static InstanceT create() where InstanceT : class { return Instance.internalCreate(); } public InstanceT internalCreate() where InstanceT: class { var ti = resolveTypeInfo(); return ti.create(); } public static InterfaceT resolve() where InterfaceT: class { return Instance.internalResolve(); } public static object resolve(Type t) { return Instance.internalResolve(t); } IDisposable registerInternal(InterfaceT instance) { var interfaceT = typeof(InterfaceT); Debug.Assert(interfaceT.IsInterface); _instances.Add(typeof(InterfaceT), instance); return new DisposeAction(() => { if (!_instances.Remove(interfaceT)) throw new Exception(string.Format("Failed to remove instance of type {0}", interfaceT)); }); } InterfaceT internalResolve() where InterfaceT : class { return internalResolve(typeof(InterfaceT)) as InterfaceT; } object internalResolve(Type t) { return _instances[t]; } #region Type Info TypeInfo resolveTypeInfo() where InstanceT : class { var t = typeof(InstanceT); object ti; if (!_info.TryGetValue(t, out ti)) { ti = new TypeInfo(); _info[t] = ti; } return ti as TypeInfo; } sealed class TypeInfo where InstanceT : class { event Action _injector; public TypeInfo() { var t = typeof(InstanceT); foreach (var field in t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)) { var injectAttributes_ = field.GetCustomAttributes(typeof(InjectAttributeBase), false) as InjectAttributeBase[]; Debug.Assert(injectAttributes_ != null); foreach (var ia in injectAttributes_) { var lia = ia; var mi = field; _injector += instance => lia.inject(mi, instance); } } } public InstanceT create() { var instance = Activator.CreateInstance(typeof(InstanceT), true /* nonpub constructors match */) as InstanceT; Debug.Assert(instance != null); if (_injector != null) _injector(instance); return instance; } } #endregion } }