using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Stencil { /// /// Stencil is a Basic Inversion of Control Container /// public class Stencil { // Singleton variables private static Stencil stencil; private static readonly Options defaults = new Options(); // State private readonly List interfaces; /// /// Gets the Stencil Singleton. /// public static Stencil Instance { get { if (stencil == null) { stencil = new Stencil(); stencil.Initilize(Defaults); } return stencil; } } /// /// Gets the default options used to create Stencil singleton. /// public static Options Defaults { get { return defaults; } } /// /// Represents an interface in the container /// private class Interface : ICloneable { /// /// Initializes a new instance of the class. /// /// The type. public Interface(Type type) { Type = type; } /// /// Gets or sets the type of this interface. /// /// /// The type. /// public Type Type { get; set; } /// /// Gets or sets the concrete type of this interface. /// /// /// The type of the concrete. /// public Type ConcreteType { get; set; } /// /// Gets or sets the value of the concrete type that implements the interface. /// /// /// The value. /// public object Value { get; set; } /// /// Gets or sets a value indicating whether this instance is a singleton. /// /// /// true if this instance is a singleton; otherwise, false. /// public bool IsSingleton { get; set; } /// /// Gets or sets the order that this interface should be processed when /// initialized in a list. This value is set by the OrderAttribute. /// /// /// The order. /// public int Order { get; set; } /// /// Determines whether this instance can create the specified type. /// /// The type. /// /// true if this instance can create the specified type; otherwise, false. /// public bool CanCreate(Type type) { return Type.IsAssignableFrom(type); } /// /// Creates a new object that is a copy of the current instance. /// /// /// A new object that is a copy of this instance. /// public object Clone() { return new Interface(Type) { ConcreteType = ConcreteType, IsSingleton = IsSingleton, Value = Value }; } } /// /// Initializes a new instance of the class. /// public Stencil() { interfaces = new List(); } /// /// Initilizes this instance. /// public void Initilize() { Initilize(Defaults); } /// /// Initilizes this instance. /// public void Initilize(Options options) { // Generate interfaces CreateInterfaces(options.Assemblies); // Set interface concrete types CreateInterfaceTypes(options.Assemblies, options.UseSingletons); } /// /// Creates the interfaces. /// /// The assemblies. private void CreateInterfaces(IEnumerable assemblies) { interfaces.Clear(); foreach (var assembly in assemblies) { CreateInterfaces(assembly); } } /// /// Creates the interfaces. /// /// The assembly. private void CreateInterfaces(Assembly assembly) { var types = assembly.GetTypes(); // Parse interfaces foreach (var type in types) { if (!type.IsInterface) continue; interfaces.Add(new Interface(type)); } } private void CreateInterfaceTypes(IEnumerable assemblies, bool useSingletons) { foreach (var assembly in assemblies) { CreateInterfaceTypes(assembly, useSingletons); } } private void CreateInterfaceTypes(Assembly assembly, bool useSingletons) { var types = assembly.GetTypes(); // Parse concrete types foreach (var type in types) { // Skip interfaces if (type.IsInterface) continue; // Check is type implements an interface we've processed foreach (var @interface in interfaces) { if (!@interface.CanCreate(type)) continue; @interface.IsSingleton = useSingletons; // Assign this concrete type to the interface if (@interface.ConcreteType == null) { @interface.Order = GetOrder(type); @interface.ConcreteType = type; } else { // Allow multiple classes to implement the same interface var clone = (Interface)@interface.Clone(); clone.Order = GetOrder(type); clone.ConcreteType = type; @interfaces.Add(clone); } break; } } } private static int GetOrder(Type type) { var result = 0; var attributes = type.GetCustomAttributes(typeof (OrderAttribute), true); foreach (OrderAttribute attribute in attributes) { result = attribute.Order; } return result; } /// /// Resolves an object of type from this instance. /// /// /// public T Resolve() { return (T)Resolve(typeof(T)); } /// /// Resolves an object of the specified type from this instance. /// /// The type. /// public object Resolve(Type type) { return CreateInstance(type); } public IList ResolveAll() { return ResolveAll(typeof (T)).Select(@object => (T) @object).ToList(); } public IList ResolveAll(Type type) { var results = new List(); foreach (var @interface in interfaces.OrderBy(i => i.Order)) { if (@interface.Type == type) { results.Add(CreateInstanceFromInterfaceValue(@interface)); } } return results; } private object CreateInstance(Type type, IList typeChain = null) { object result; // validate we're not recursively creating a type if (typeChain != null && typeChain.Contains(type)) { return null; } if (type.IsInterface) { result = CreateInstanceFromInterface(type); } else { result = CreateInstanceFromConcrete(type, typeChain); } return result; } private object CreateInstanceFromInterface(Type type) { object result = null; foreach (var @interface in interfaces) { if (@interface.Type != type) continue; result = CreateInstanceFromInterfaceValue(@interface); break; } return result; } private object CreateInstanceFromInterfaceValue(Interface @interface) { var value = @interface.Value; if (value == null) { var chain = new List { @interface.Type }; value = CreateInstanceFromConcrete(@interface.ConcreteType, chain); if (@interface.IsSingleton) { @interface.Value = value; } } return value; } private object CreateInstanceFromConcrete(Type type, IList typeChain = null) { object result = Activator.CreateInstance(type); if (typeChain == null) { typeChain = new List(); } typeChain.Add(type); var properties = result.GetType().GetProperties(); foreach (var property in properties) { var propertyType = property.PropertyType; // Set public interfaces if (propertyType.IsInterface && propertyType.IsPublic && !propertyType.IsGenericType) { var value = CreateInstance(propertyType, typeChain); property.SetValue(result, value, null); } // Set generic lists if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(IList<>)) { // Create a generic list var genericType = propertyType.GetGenericArguments()[0]; var listType = typeof(List<>).MakeGenericType(genericType); var list = Activator.CreateInstance(listType); var items = ResolveAll(genericType); foreach (var item in items) { listType.InvokeMember("Add", BindingFlags.InvokeMethod, null, list, new[] { item }); } property.SetValue(result, list, null); } } return result; } } /// /// Holds the options used to initialize the Stencil container. /// public class Options { /// /// Initializes a new instance of the class. /// public Options() { // Default to load types from the current assembly Assemblies = new List { Assembly.GetExecutingAssembly() }; // Use property injection & singletons by default UsePropertyInjection = true; UseSingletons = true; } /// /// Gets the assemblies used to load the container types from. /// public IList Assemblies { get; private set; } /// /// Gets or sets a value indicating whether Stencil should use property injection. /// /// /// true if Stencil should use property injection; otherwise, false. /// public bool UsePropertyInjection { get; set; } /// /// Gets or sets a value indicating whether the container should use singletons. /// /// /// true if the container should use singletons; otherwise, false. /// public bool UseSingletons { get; set; } } /// /// Attribute to allow the specification of class orders in generic lists /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class OrderAttribute : Attribute { /// /// Initializes a new instance of the class. /// /// The order. public OrderAttribute(int order) { Order = order; } /// /// Gets or sets the order. /// /// /// The order. /// public int Order { get; set; } } }