/**************************************************************************** * Copyright (c) 2018 ~ 2022 liangxiegame UNDER MIT License * * http://qframework.io * https://github.com/liangxiegame/QFramework ****************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace QFramework { /// /// Used by the injection container to determine if a property or field should be injected. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class InjectAttribute : Attribute { public InjectAttribute(string name) { Name = name; } public string Name { get; set; } public InjectAttribute() { } } public interface IQFrameworkContainer { /// /// Clears all type mappings and instances. /// void Clear(); /// /// Injects registered types/mappings into an object /// /// void Inject(object obj); /// /// Injects everything that is registered at once /// void InjectAll(); /// /// Register a type mapping /// /// The base type. /// The concrete type void Register(string name = null); void RegisterRelation(); /// /// Register an instance of a type. /// /// /// /// /// void RegisterInstance(TBase @default, bool injectNow) where TBase : class; /// /// Register an instance of a type. /// /// /// /// /// void RegisterInstance(Type type, object @default, bool injectNow); /// /// Register a named instance /// /// The type to register the instance for. /// The name for the instance to be resolved. /// The instance that will be resolved be the name /// Perform the injection immediately void RegisterInstance(Type baseType, object instance = null, string name = null, bool injectNow = true); void RegisterInstance(TBase instance, string name, bool injectNow = true) where TBase : class; void RegisterInstance(TBase instance) where TBase : class; /// /// If an instance of T exist then it will return that instance otherwise it will create a new one based off mappings. /// /// The type of instance to resolve /// The/An instance of 'instanceType' T Resolve(string name = null, bool requireInstance = false, params object[] args) where T : class; TBase ResolveRelation(Type tfor, params object[] arg); TBase ResolveRelation(params object[] arg); /// /// Resolves all instances of TType or subclasses of TType. Either named or not. /// /// The Type to resolve /// List of objects. IEnumerable ResolveAll(); //IEnumerable ResolveAll(Type type); void Register(Type source, Type target, string name = null); /// /// Resolves all instances of TType or subclasses of TType. Either named or not. /// /// The Type to resolve /// List of objects. IEnumerable ResolveAll(Type type); TypeMappingCollection Mappings { get; set; } TypeInstanceCollection Instances { get; set; } TypeRelationCollection RelationshipMappings { get; set; } /// /// If an instance of instanceType exist then it will return that instance otherwise it will create a new one based off mappings. /// /// The type of instance to resolve /// The type of instance to resolve /// If true will return null if an instance isn't registered. /// The/An instance of 'instanceType' object Resolve(Type baseType, string name = null, bool requireInstance = false, params object[] constructorArgs); object ResolveRelation(Type tfor, Type tbase, params object[] arg); void RegisterRelation(Type tfor, Type tbase, Type tconcrete); object CreateInstance(Type type, params object[] args); } /// /// A ViewModel Container and a factory for Controllers and commands. /// public class QFrameworkContainer : IQFrameworkContainer { private TypeInstanceCollection _instances; private TypeMappingCollection _mappings; public TypeMappingCollection Mappings { get { return _mappings ?? (_mappings = new TypeMappingCollection()); } set { _mappings = value; } } public TypeInstanceCollection Instances { get { return _instances ?? (_instances = new TypeInstanceCollection()); } set { _instances = value; } } public TypeRelationCollection RelationshipMappings { get { return _relationshipMappings; } set { _relationshipMappings = value; } } public IEnumerable ResolveAll() { foreach (var obj in ResolveAll(typeof(TType))) { yield return (TType) obj; } } /// /// Resolves all instances of TType or subclasses of TType. Either named or not. /// /// The Type to resolve /// List of objects. public IEnumerable ResolveAll(Type type) { foreach (KeyValuePair, object> kv in Instances) { if (kv.Key.Item1 == type && !string.IsNullOrEmpty(kv.Key.Item2)) yield return kv.Value; } foreach (KeyValuePair, Type> kv in Mappings) { if (!string.IsNullOrEmpty(kv.Key.Item2)) { #if NETFX_CORE var condition = type.GetTypeInfo().IsSubclassOf(mapping.From); #else var condition = type.IsAssignableFrom(kv.Key.Item1); #endif if (condition) { var item = Activator.CreateInstance(kv.Value); Inject(item); yield return item; } } } } /// /// Clears all type-mappings and instances. /// public void Clear() { Instances.Clear(); Mappings.Clear(); RelationshipMappings.Clear(); } /// /// Injects registered types/mappings into an object /// /// public void Inject(object obj) { if (obj == null) return; #if !NETFX_CORE var members = obj.GetType().GetMembers(); #else var members = obj.GetType().GetTypeInfo().DeclaredMembers; #endif foreach (var memberInfo in members) { var injectAttribute = memberInfo.GetCustomAttributes(typeof(InjectAttribute), true).FirstOrDefault() as InjectAttribute; if (injectAttribute != null) { if (memberInfo is PropertyInfo) { var propertyInfo = memberInfo as PropertyInfo; propertyInfo.SetValue(obj, Resolve(propertyInfo.PropertyType, injectAttribute.Name), null); } else if (memberInfo is FieldInfo) { var fieldInfo = memberInfo as FieldInfo; fieldInfo.SetValue(obj, Resolve(fieldInfo.FieldType, injectAttribute.Name)); } } } } /// /// Register a type mapping /// /// The base type. /// The concrete type public void Register(string name = null) { Mappings[typeof(TSource), name] = typeof(TSource); } /// /// Register a type mapping /// /// The base type. /// The concrete type public void Register(string name = null) { Mappings[typeof(TSource), name] = typeof(TTarget); } public void Register(Type source, Type target, string name = null) { Mappings[source, name] = target; } /// /// Register a named instance /// /// The type to register the instance for. /// The instance that will be resolved be the name /// Perform the injection immediately public void RegisterInstance(Type baseType, object instance = null, bool injectNow = true) { RegisterInstance(baseType, instance, null, injectNow); } /// /// Register a named instance /// /// The type to register the instance for. /// The name for the instance to be resolved. /// The instance that will be resolved be the name /// Perform the injection immediately public virtual void RegisterInstance(Type baseType, object instance = null, string name = null, bool injectNow = true) { Instances[baseType, name] = instance; if (injectNow) { Inject(instance); } } public void RegisterInstance(TBase instance) where TBase : class { RegisterInstance(instance, true); } public void RegisterInstance(TBase instance, bool injectNow) where TBase : class { RegisterInstance(instance, null, injectNow); } public void RegisterInstance(TBase instance, string name, bool injectNow = true) where TBase : class { RegisterInstance(typeof(TBase), instance, name, injectNow); } /// /// If an instance of T exist then it will return that instance otherwise it will create a new one based off mappings. /// /// The type of instance to resolve /// The/An instance of 'instanceType' public T Resolve(string name = null, bool requireInstance = false, params object[] args) where T : class { return (T) Resolve(typeof(T), name, requireInstance, args); } /// /// If an instance of instanceType exist then it will return that instance otherwise it will create a new one based off mappings. /// /// The type of instance to resolve /// The type of instance to resolve /// If true will return null if an instance isn't registered. /// The arguments to pass to the constructor if any. /// The/An instance of 'instanceType' public object Resolve(Type baseType, string name = null, bool requireInstance = false, params object[] constructorArgs) { // Look for an instance first var item = Instances[baseType, name]; if (item != null) { return item; } if (requireInstance) return null; // Check if there is a mapping of the type var namedMapping = Mappings[baseType, name]; if (namedMapping != null) { var obj = CreateInstance(namedMapping, constructorArgs); //Inject(obj); return obj; } return null; } public object CreateInstance(Type type, params object[] constructorArgs) { if (constructorArgs != null && constructorArgs.Length > 0) { //return Activator.CreateInstance(type,BindingFlags.Public | BindingFlags.Instance,Type.DefaultBinder, constructorArgs,CultureInfo.CurrentCulture); var obj2 = Activator.CreateInstance(type, constructorArgs); Inject(obj2); return obj2; } #if !NETFX_CORE ConstructorInfo[] constructor = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance); #else ConstructorInfo[] constructor = type.GetTypeInfo().DeclaredConstructors.ToArray(); #endif if (constructor.Length < 1) { var obj2 = Activator.CreateInstance(type); Inject(obj2); return obj2; } var maxParameters = constructor.First().GetParameters(); foreach (var c in constructor) { var parameters = c.GetParameters(); if (parameters.Length > maxParameters.Length) { maxParameters = parameters; } } var args = maxParameters.Select(p => { if (p.ParameterType.IsArray) { return ResolveAll(p.ParameterType); } return Resolve(p.ParameterType) ?? Resolve(p.ParameterType, p.Name); }).ToArray(); var obj = Activator.CreateInstance(type, args); Inject(obj); return obj; } public TBase ResolveRelation(Type tfor, params object[] args) { try { return (TBase) ResolveRelation(tfor, typeof(TBase), args); } catch (InvalidCastException castIssue) { throw new Exception( string.Format("Resolve Relation couldn't cast to {0} from {1}", typeof(TBase).Name, tfor.Name), castIssue); } } public void InjectAll() { foreach (object instance in Instances.Values) { Inject(instance); } } private TypeRelationCollection _relationshipMappings = new TypeRelationCollection(); public void RegisterRelation() { RelationshipMappings[typeof(TFor), typeof(TBase)] = typeof(TConcrete); } public void RegisterRelation(Type tfor, Type tbase, Type tconcrete) { RelationshipMappings[tfor, tbase] = tconcrete; } public object ResolveRelation(Type tfor, Type tbase, params object[] args) { var concreteType = RelationshipMappings[tfor, tbase]; if (concreteType == null) { return null; } var result = CreateInstance(concreteType, args); //Inject(result); return result; } public TBase ResolveRelation(params object[] arg) { return (TBase) ResolveRelation(typeof(TFor), typeof(TBase), arg); } } // http://stackoverflow.com/questions/1171812/multi-key-dictionary-in-c public class Tuple //FUCKING Unity: struct is not supported in Mono { public readonly T1 Item1; public readonly T2 Item2; public Tuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2; } public override bool Equals(Object obj) { Tuple p = obj as Tuple; if (obj == null) return false; if (Item1 == null) { if (p.Item1 != null) return false; } else { if (p.Item1 == null || !Item1.Equals(p.Item1)) return false; } if (Item2 == null) { if (p.Item2 != null) return false; } else { if (p.Item2 == null || !Item2.Equals(p.Item2)) return false; } return true; } public override int GetHashCode() { int hash = 0; if (Item1 != null) hash ^= Item1.GetHashCode(); if (Item2 != null) hash ^= Item2.GetHashCode(); return hash; } } // Kanglai: Using Dictionary rather than List! public class TypeMappingCollection : Dictionary, Type> { public Type this[Type from, string name = null] { get { Tuple key = new Tuple(from, name); Type mapping = null; if (this.TryGetValue(key, out mapping)) { return mapping; } return null; } set { Tuple key = new Tuple(from, name); this[key] = value; } } } public class TypeInstanceCollection : Dictionary, object> { public object this[Type from, string name = null] { get { Tuple key = new Tuple(from, name); object mapping = null; if (this.TryGetValue(key, out mapping)) { return mapping; } return null; } set { Tuple key = new Tuple(from, name); this[key] = value; } } } public class TypeRelationCollection : Dictionary, Type> { public Type this[Type from, Type to] { get { Tuple key = new Tuple(from, to); Type mapping = null; if (this.TryGetValue(key, out mapping)) { return mapping; } return null; } set { Tuple key = new Tuple(from, to); this[key] = value; } } } }