| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 | namespace Caliburn.Micro.Core {    using System;    using System.Collections;    using System.Collections.Generic;    using System.Linq;    using System.Reflection;    /// <summary>    ///   A simple IoC container.    /// </summary>    public class SimpleContainer {        static readonly Type delegateType = typeof(Delegate);        static readonly Type enumerableType = typeof(IEnumerable);        readonly List<ContainerEntry> entries;        /// <summary>        ///   Initializes a new instance of the <see cref = "SimpleContainer" /> class.        /// </summary>        public SimpleContainer() {            entries = new List<ContainerEntry>();        }        SimpleContainer(IEnumerable<ContainerEntry> entries) {            this.entries = new List<ContainerEntry>(entries);        }        /// <summary>        ///   Registers the instance.        /// </summary>        /// <param name = "service">The service.</param>        /// <param name = "key">The key.</param>        /// <param name = "implementation">The implementation.</param>        public void RegisterInstance(Type service, string key, object implementation) {            RegisterHandler(service, key, container => implementation);        }        /// <summary>        ///   Registers the class so that a new instance is created on every request.        /// </summary>        /// <param name = "service">The service.</param>        /// <param name = "key">The key.</param>        /// <param name = "implementation">The implementation.</param>        public void RegisterPerRequest(Type service, string key, Type implementation) {            RegisterHandler(service, key, container => container.BuildInstance(implementation));        }        /// <summary>        ///   Registers the class so that it is created once, on first request, and the same instance is returned to all requestors thereafter.        /// </summary>        /// <param name = "service">The service.</param>        /// <param name = "key">The key.</param>        /// <param name = "implementation">The implementation.</param>        public void RegisterSingleton(Type service, string key, Type implementation) {            object singleton = null;            RegisterHandler(service, key, container => singleton ?? (singleton = container.BuildInstance(implementation)));        }        /// <summary>        ///   Registers a custom handler for serving requests from the container.        /// </summary>        /// <param name = "service">The service.</param>        /// <param name = "key">The key.</param>        /// <param name = "handler">The handler.</param>        public void RegisterHandler(Type service, string key, Func<SimpleContainer, object> handler) {            GetOrCreateEntry(service, key).Add(handler);        }        /// <summary>        ///   Unregisters any handlers for the service/key that have previously been registered.        /// </summary>        /// <param name = "service">The service.</param>        /// <param name = "key">The key.</param>        public void UnregisterHandler(Type service, string key) {            var entry = GetEntry(service, key);            if (entry != null) {                entries.Remove(entry);            }        }        /// <summary>        ///   Requests an instance.        /// </summary>        /// <param name = "service">The service.</param>        /// <param name = "key">The key.</param>        /// <returns>The instance, or null if a handler is not found.</returns>        public object GetInstance(Type service, string key) {            var entry = GetEntry(service, key);            if (entry != null) {                return entry.Single()(this);            }            if (service == null) {                return null;            }            if (delegateType.IsAssignableFrom(service)) {                var typeToCreate = service.GetGenericArguments()[0];                var factoryFactoryType = typeof(FactoryFactory<>).MakeGenericType(typeToCreate);                var factoryFactoryHost = Activator.CreateInstance(factoryFactoryType);                var factoryFactoryMethod = factoryFactoryType.GetMethod("Create", new Type[] { typeof(SimpleContainer) });                return factoryFactoryMethod.Invoke(factoryFactoryHost, new object[] { this });            }            if (enumerableType.IsAssignableFrom(service) && service.IsGenericType()) {                var listType = service.GetGenericArguments()[0];                var instances = GetAllInstances(listType).ToList();                var array = Array.CreateInstance(listType, instances.Count);                for (var i = 0; i < array.Length; i++) {                    array.SetValue(instances[i], i);                }                return array;            }            return null;        }        /// <summary>        /// Determines if a handler for the service/key has previously been registered.        /// </summary>        /// <param name="service">The service.</param>        /// <param name="key">The key.</param>        /// <returns>True if a handler is registere; false otherwise.</returns>        public bool HasHandler(Type service, string key) {            return GetEntry(service, key) != null;        }        /// <summary>        ///   Requests all instances of a given type.        /// </summary>        /// <param name = "service">The service.</param>        /// <returns>All the instances or an empty enumerable if none are found.</returns>        public IEnumerable<object> GetAllInstances(Type service) {            var entry = GetEntry(service, null);            return entry != null ? entry.Select(x => x(this)) : new object[0];        }        /// <summary>        ///   Pushes dependencies into an existing instance based on interface properties with setters.        /// </summary>        /// <param name = "instance">The instance.</param>        public void BuildUp(object instance) {            var injectables = from property in instance.GetType().GetProperties()                              where property.CanRead && property.CanWrite && property.PropertyType.IsInterface()                              select property;            foreach (var propertyInfo in injectables) {                var injection = GetAllInstances(propertyInfo.PropertyType).ToArray();                if (injection.Any()) {                    propertyInfo.SetValue(instance, injection.First(), null);                }            }        }        /// <summary>        /// Creates a child container.        /// </summary>        /// <returns>A new container.</returns>        public SimpleContainer CreateChildContainer() {            return new SimpleContainer(entries);        }        ContainerEntry GetOrCreateEntry(Type service, string key) {            var entry = GetEntry(service, key);            if (entry == null) {                entry = new ContainerEntry { Service = service, Key = key };                entries.Add(entry);            }            return entry;        }        ContainerEntry GetEntry(Type service, string key) {            if (service == null) {                return entries.FirstOrDefault(x => x.Key == key);            }            if (key == null) {                return entries.FirstOrDefault(x => x.Service == service && x.Key == null)                       ?? entries.FirstOrDefault(x => x.Service == service);            }            return entries.FirstOrDefault(x => x.Service == service && x.Key == key);        }        /// <summary>        ///   Actually does the work of creating the instance and satisfying it's constructor dependencies.        /// </summary>        /// <param name = "type">The type.</param>        /// <returns></returns>        protected object BuildInstance(Type type) {            var args = DetermineConstructorArgs(type);            return ActivateInstance(type, args);        }        /// <summary>        ///   Creates an instance of the type with the specified constructor arguments.        /// </summary>        /// <param name = "type">The type.</param>        /// <param name = "args">The constructor args.</param>        /// <returns>The created instance.</returns>        protected virtual object ActivateInstance(Type type, object[] args) {            var instance = args.Length > 0 ? System.Activator.CreateInstance(type, args) : System.Activator.CreateInstance(type);            Activated(instance);            return instance;        }        /// <summary>        ///   Occurs when a new instance is created.        /// </summary>        public event Action<object> Activated = delegate { };        object[] DetermineConstructorArgs(Type implementation) {            var args = new List<object>();            var constructor = SelectEligibleConstructor(implementation);            if (constructor != null)                args.AddRange(constructor.GetParameters().Select(info => GetInstance(info.ParameterType, null)));            return args.ToArray();        }        static ConstructorInfo SelectEligibleConstructor(Type type) {            return (from c in type.GetConstructors().Where(c => c.IsPublic)                    orderby c.GetParameters().Length descending                    select c).FirstOrDefault();        }        class ContainerEntry : List<Func<SimpleContainer, object>> {            public string Key;            public Type Service;        }        class FactoryFactory<T> {            public Func<T> Create(SimpleContainer container) {                return () => (T)container.GetInstance(typeof(T), null);            }        }    }}
 |