Files
RebootKit/Runtime/Engine/Code/Foundation/DIContext.cs
2025-03-15 12:33:50 +01:00

117 lines
3.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace RebootKit.Engine.Foundation {
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method)]
public class InjectAttribute : Attribute {
}
public class DIContext {
private const BindingFlags k_fieldsBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
private const BindingFlags k_methodsBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
public interface IFieldInjector {
bool Inject(FieldInfo field, object target, DIContext context);
}
private static readonly Logger Logger = new(nameof(DIContext));
private readonly Dictionary<Type, object> _bindingsMaps = new();
private readonly List<IFieldInjector> _fieldInjectors = new();
public DIContext() {
Bind(this);
AddInjector(new InjectAttributeFieldInjector());
}
public void AddInjector(IFieldInjector injector) {
_fieldInjectors.Add(injector);
}
public void Bind(Type type, object obj) {
if (!_bindingsMaps.TryAdd(type, obj)) {
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 (_bindingsMaps.TryGetValue(type, out object obj)) {
return obj;
}
Logger.Error($"Couldn't resolve `{type}`");
return null;
}
public T Resolve<T>() {
return (T) Resolve(typeof(T));
}
public void Inject<T>(T target) {
Type type = typeof(T);
foreach (FieldInfo field in type.GetFields(k_fieldsBindingFlags)) {
if (!InjectField(field, target)) {
return;
}
}
foreach (MethodInfo method in type.GetMethods(k_methodsBindingFlags)) {
if (!Attribute.IsDefined(method, typeof(InjectAttribute))) {
continue;
}
Type[] paramsTypes = method.GetParameters()
.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) {
Logger.Error($"Failed to resolve method parameter of type `{paramsTypes[i]}`");
}
}
method.Invoke(target, instances);
}
}
private bool InjectField(FieldInfo field, object target) {
for (int i = _fieldInjectors.Count - 1; i >= 0; i--) {
if (_fieldInjectors[i].Inject(field, target, this)) {
return true;
}
}
return false;
}
private 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) {
Logger.Error($"Cannot resolve `{field.FieldType}`");
return false;
}
field.SetValue(target, instance);
return true;
}
}
}
}