using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Tango.Core.DI { /// /// Represents the default Tango IOC container. /// /// public class TangoIOC : ExtendedObject, ITangoIOC { private class RegisteredType { public Type InterfaceType { get; set; } public Type ImplementationType { get; set; } public Object Instance { get; set; } public RegisteredType(Type interfaceType, Type implementationType, Object instance) { InterfaceType = interfaceType; ImplementationType = implementationType; Instance = instance; } } private static TangoIOC _default; /// /// Gets the default Tango IOC container instance. /// public static TangoIOC Default { get { if (_default == null) { _default = new TangoIOC(); } return _default; } } private Dictionary _registeredTypes; private List>> _waitingRetrivals; /// /// Gets or sets a value indicating whether to throw an exception when a type is requested but not registered. /// public bool ThrowOnRequestedTypeNotFound { get; set; } = true; /// /// Initializes a new instance of the class. /// public TangoIOC() { _registeredTypes = new Dictionary(); _waitingRetrivals = new List>>(); } /// /// Registers the specified type. /// /// public void Register() where T : class { RegisterInternal(typeof(T), typeof(T), null); } /// /// Registers the specified instance type and instance. /// /// /// The instance. public void Register(T instance) where T : class { RegisterInternal(typeof(T), instance.GetType(), instance); } /// /// Registers the specified type. /// /// The type. public void Register(Type type) { RegisterInternal(type, type, null); } /// /// Registers the specified type and implementation type. /// /// The type. /// The implementation. public void Register(Type type, Type implementation) { RegisterInternal(type, implementation.GetType(), null); } /// /// Registers the specified type and instance. /// /// The type. /// The instance. public void Register(Type type, Object instance) { RegisterInternal(type, instance.GetType(), instance); } /// /// Registers this instance. /// /// The type of the type. /// The type of the implementation. public void Register() where Implementation : class, Type { RegisterInternal(typeof(Type), typeof(Implementation), null); } /// /// Registers the specified type, implementation and implementation instance. /// /// The type of the type. /// The type of the implementation. /// The instance. public void Register(Implementation instance) where Implementation : class, Type { RegisterInternal(typeof(Type), typeof(Implementation), instance); } /// /// Unregisters the specified type. /// /// public void Unregister() { Unregister(typeof(T)); } /// /// Unregisters the specified type. /// /// The type. public virtual void Unregister(Type type) { _registeredTypes.Remove(type); } /// /// Gets the instance of type T. /// /// /// public T GetInstance() { return (T)GetInstance(typeof(T)); } /// /// Notifies the awaiting retrievals of a new instance available. /// /// Type of the registered. private void NotifyAwaitingRetrievals(RegisteredType registeredType) { var toNotify = _waitingRetrivals.Where(x => x.Item1 == registeredType.InterfaceType).ToList(); toNotify.ForEach(x => x.Item2(registeredType.Instance)); _waitingRetrivals.RemoveAll(x => toNotify.Contains(x)); } /// /// Gets the instance by the specified type. /// /// The type. /// /// The type specified could not be located! public virtual object GetInstance(Type type) { RegisteredType registeredType = null; if (_registeredTypes.TryGetValue(type, out registeredType)) { if (registeredType.Instance == null) { registeredType.Instance = CreateInstance(registeredType.ImplementationType); NotifyAwaitingRetrievals(registeredType); } return registeredType.Instance; } else { //Try get by inherited.. registeredType = _registeredTypes.Select(x => x.Value).SingleOrDefault(x => type.IsAssignableFrom(x.InterfaceType)); if (registeredType != null) { if (registeredType.Instance == null) { registeredType.Instance = CreateInstance(registeredType.ImplementationType); NotifyAwaitingRetrievals(registeredType); } return registeredType.Instance; } if (!DesignMode && ThrowOnRequestedTypeNotFound) { throw new InvalidOperationException(String.Format("Requested type '{0}' could not be found.", type.Name)); } else { return null; } } } /// /// Executes the specified action when type T is available. if already available action will be executed immediately. /// /// /// The callback. /// public void GetInstanceWhenAvailable(Action callback) { GetInstanceWhenAvailable(typeof(T), (x) => callback((T)x)); } /// /// Executes the specified action when type T is available. if already available action will be executed immediately. /// /// The callback. /// public virtual void GetInstanceWhenAvailable(Type type, Action callback) { RegisteredType registeredType = null; if (_registeredTypes.TryGetValue(type, out registeredType)) { if (registeredType.Instance != null) { callback(registeredType.Instance); return; } } else { //Try get by inherited.. registeredType = _registeredTypes.Select(x => x.Value).SingleOrDefault(x => type.IsAssignableFrom(x.InterfaceType)); if (registeredType != null) { if (registeredType.Instance != null) { callback(registeredType.Instance); return; } } } _waitingRetrivals.Add(new Tuple>(type, callback)); } /// /// Determines whether the specified type is registered. /// /// /// /// true if this instance is registered; otherwise, false. /// public bool IsRegistered() { return IsRegistered(typeof(T)); } /// /// Determines whether the specified type is registered. /// /// The type. /// /// true if the specified type is registered; otherwise, false. /// public virtual bool IsRegistered(Type type) { return _registeredTypes.ContainsKey(type); } /// /// Registers the internal. /// /// Type of the interface. /// Type of the implementation. /// The instance. protected virtual void RegisterInternal(Type interfaceType, Type implementationType, Object instance) { if (DesignMode) { return; } if (!interfaceType.IsAssignableFrom(implementationType)) { throw new InvalidOperationException(String.Format("Interface type '{0}' cannot be assigned from implementation type '{1}'.", interfaceType.Name, implementationType.Name)); } if (IsRegistered(interfaceType)) { throw new InvalidOperationException(String.Format("The specified type '{0}' is already registered.", interfaceType.Name)); } if (instance != null && instance.GetType() != implementationType) { throw new InvalidOperationException(String.Format("Instance type '{0}' does not match the implementation type '{1}'.", instance.GetType().Name, implementationType.Name)); } var registeredType = new RegisteredType(interfaceType, implementationType, instance); _registeredTypes.Add(interfaceType, registeredType); if (implementationType.GetCustomAttribute() != null && instance == null) { instance = GetInstance(interfaceType); } if (registeredType.Instance != null) { NotifyAwaitingRetrievals(registeredType); } } /// /// Initializes the instance. /// /// The type. /// protected virtual object CreateInstance(Type type) { List arguments = new List(); ConstructorInfo constructor = null; constructor = type.GetConstructors().FirstOrDefault(x => x.GetCustomAttribute() != null); if (constructor == null) { constructor = type.GetConstructors().FirstOrDefault(x => x.GetParameters().Count() > 0); } object instance = null; if (constructor != null) { foreach (var arg in constructor.GetParameters()) { arguments.Add(GetInstance(arg.ParameterType)); } instance = Activator.CreateInstance(type, arguments.ToArray()); } else { instance = Activator.CreateInstance(type); } Inject(instance); return instance; } /// /// Gets all instances by the specified base type. /// /// /// public IEnumerable GetAllInstancesByBase() { return GetAllInstancesByBase(typeof(T)).Select(x => (T)x); } /// /// Gets all instances by the specified base type. /// /// Type of the base. /// public virtual IEnumerable GetAllInstancesByBase(Type baseType) { return _registeredTypes.Where(x => baseType.IsAssignableFrom(x.Key)).Select(x => x.Value.Instance != null ? x.Value.Instance : GetInstance(x.Value.ImplementationType)); } /// /// Injects any dependencies to the target properties using the . /// /// The target. public void Inject(object target) { var type = target.GetType(); foreach (var prop in type.GetPropertiesWithAttribute(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)) { var att = prop.GetCustomAttribute(); if (att.Mode == TangoInjectMode.Immediate) { prop.SetValue(target, GetInstance(prop.PropertyType)); } else { GetInstanceWhenAvailable(prop.PropertyType, (x) => { prop.SetValue(target, GetInstance(prop.PropertyType)); }); } } foreach (var field in type.GetFieldsWithAttribute(BindingFlags.NonPublic | BindingFlags.Instance)) { var att = field.GetCustomAttribute(); if (att.Mode == TangoInjectMode.Immediate) { field.SetValue(target, GetInstance(field.FieldType)); } else { GetInstanceWhenAvailable(field.FieldType, (x) => { field.SetValue(target, GetInstance(field.FieldType)); }); } } } } }