/****************************************************************************
* 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;
}
}
}
}