127 lines
4.1 KiB
C#
Executable File
127 lines
4.1 KiB
C#
Executable File
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using ZLinq;
|
|
|
|
namespace RebootKit.Engine.Foundation {
|
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method)]
|
|
public class InjectAttribute : Attribute {
|
|
}
|
|
|
|
public class DIContext {
|
|
const BindingFlags k_fieldsBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
|
const BindingFlags k_methodsBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
|
|
|
static readonly Logger s_logger = new(nameof(DIContext));
|
|
|
|
readonly Dictionary<Type, object> m_BindingsMaps = new();
|
|
readonly List<IFieldInjector> m_FieldInjectors = new();
|
|
|
|
public DIContext() {
|
|
Bind(this);
|
|
|
|
AddInjector(new InjectAttributeFieldInjector());
|
|
}
|
|
|
|
public void AddInjector(IFieldInjector injector) {
|
|
m_FieldInjectors.Add(injector);
|
|
}
|
|
|
|
public void Bind(Type type, object obj) {
|
|
if (!m_BindingsMaps.TryAdd(type, obj)) {
|
|
s_logger.Error($"Cannot bind to '{type}', slot is already occupied");
|
|
}
|
|
}
|
|
|
|
public void Bind<TBind>(TBind obj) {
|
|
Bind(typeof(TBind), obj);
|
|
}
|
|
|
|
public object Resolve(Type type) {
|
|
if (m_BindingsMaps.TryGetValue(type, out object obj)) return obj;
|
|
|
|
s_logger.Error($"Couldn't resolve `{type}`");
|
|
return null;
|
|
}
|
|
|
|
public T Resolve<T>() {
|
|
return (T) Resolve(typeof(T));
|
|
}
|
|
|
|
// @brief creates new instance of an object and injects dependencies
|
|
public T Create<T>() {
|
|
T instance = Activator.CreateInstance<T>();
|
|
|
|
Inject(instance);
|
|
return instance;
|
|
}
|
|
|
|
public T Create<T>(params object[] args) {
|
|
T instance = (T) Activator.CreateInstance(typeof(T), args);
|
|
Inject(instance);
|
|
return instance;
|
|
}
|
|
|
|
public void Inject(object target) {
|
|
Type type = target.GetType();
|
|
|
|
foreach (FieldInfo field in type.GetFields(k_fieldsBindingFlags)) {
|
|
InjectField(field, target);
|
|
}
|
|
|
|
foreach (MethodInfo method in type.GetMethods(k_methodsBindingFlags)) {
|
|
if (!Attribute.IsDefined(method, typeof(InjectAttribute))) {
|
|
continue;
|
|
}
|
|
|
|
Type[] paramsTypes = method.GetParameters()
|
|
.AsValueEnumerable()
|
|
.Select(t => t.ParameterType)
|
|
.ToArray();
|
|
|
|
object[] instances = new object[paramsTypes.Length];
|
|
|
|
for (int i = 0; i < paramsTypes.Length; ++i) {
|
|
instances[i] = Resolve(paramsTypes[i]);
|
|
|
|
if (instances[i] == null) {
|
|
s_logger.Error($"Failed to resolve method parameter of type `{paramsTypes[i]}`");
|
|
}
|
|
}
|
|
|
|
method.Invoke(target, instances);
|
|
}
|
|
}
|
|
|
|
bool InjectField(FieldInfo field, object target) {
|
|
for (int i = m_FieldInjectors.Count - 1; i >= 0; i--) {
|
|
if (m_FieldInjectors[i].Inject(field, target, this)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public interface IFieldInjector {
|
|
bool Inject(FieldInfo field, object target, DIContext context);
|
|
}
|
|
|
|
class InjectAttributeFieldInjector : IFieldInjector {
|
|
public bool Inject(FieldInfo field, object target, DIContext context) {
|
|
if (!Attribute.IsDefined(field, typeof(InjectAttribute))) return false;
|
|
|
|
object instance = context.Resolve(field.FieldType);
|
|
|
|
if (instance == null) {
|
|
s_logger.Error($"Cannot resolve `{field.FieldType}`");
|
|
return false;
|
|
}
|
|
|
|
field.SetValue(target, instance);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} |