DragonECS/src/Injections/Injector.cs

301 lines
10 KiB
C#
Raw Normal View History

2025-03-14 16:53:25 +08:00
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.Internal;
2024-04-28 18:36:14 +08:00
using System;
using System.Collections.Generic;
2024-09-08 19:39:43 +08:00
using System.Linq;
2024-08-20 11:53:23 +08:00
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
2024-08-20 11:53:23 +08:00
public class Injector : IInjector
{
private EcsPipeline _pipeline;
private Dictionary<Type, InjectionBranch> _branches = new Dictionary<Type, InjectionBranch>(32);
private Dictionary<Type, InjectionNodeBase> _nodes = new Dictionary<Type, InjectionNodeBase>(32);
private bool _isInit = false;
#if DEBUG
2024-09-08 19:39:43 +08:00
private HashSet<Type> _requiredInjectionTypes = new HashSet<Type>();
#endif
2024-08-20 11:53:23 +08:00
public EcsPipeline Pipelie
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _pipeline; }
}
2024-04-28 18:36:14 +08:00
private Injector() { }
2024-02-22 23:48:10 +08:00
2024-08-20 11:53:23 +08:00
#region Inject/Extract/AddNode
public void Inject<T>(T obj)
{
2024-03-14 00:24:23 +08:00
object raw = obj;
2024-09-08 19:39:43 +08:00
Type tType = typeof(T);
Type objType = obj.GetType();
if (_branches.TryGetValue(objType, out InjectionBranch branch) == false)
{
2024-09-08 19:39:43 +08:00
if (_nodes.ContainsKey(tType) == false)
2024-02-25 02:33:24 +08:00
{
2024-09-08 19:39:43 +08:00
InitNode(new InjectionNode<T>());
2024-02-25 02:33:24 +08:00
}
2024-09-08 19:39:43 +08:00
bool hasNode = _nodes.ContainsKey(objType);
if (hasNode == false && obj is IInjectionUnit unit)
2024-02-25 02:33:24 +08:00
{
unit.InitInjectionNode(new InjectionGraph(this));
2024-09-08 19:39:43 +08:00
hasNode = _nodes.ContainsKey(objType);
}
2024-09-07 21:25:21 +08:00
2024-09-08 19:39:43 +08:00
branch = new InjectionBranch(this, objType);
InitBranch(branch);
#if DEBUG
2024-09-08 19:39:43 +08:00
foreach (var requiredInjectionType in _requiredInjectionTypes)
{
if (requiredInjectionType.IsAssignableFrom(objType))
{
if (_nodes.ContainsKey(requiredInjectionType) == false)
{
2025-03-14 21:57:52 +08:00
throw new InjectionException($"A systems in the pipeline implements IEcsInject<{requiredInjectionType.Name}> interface, but no suitable injection node was found in the Injector. To create a node, use Injector.AddNode<{requiredInjectionType.Name}>() or implement the IInjectionUnit interface for type {objType.Name}.");
2024-09-08 19:39:43 +08:00
}
2024-04-28 18:36:14 +08:00
}
2024-02-25 02:33:24 +08:00
}
2024-09-08 19:39:43 +08:00
#endif
}
2024-03-14 00:24:23 +08:00
branch.Inject(raw);
}
2025-03-13 20:46:51 +08:00
public void ExtractAllTo(object target)
{
if (target is IEcsInjectProcess == false) { return; }
foreach (var node in _nodes)
{
node.Value.ExtractTo(target);
}
}
2024-08-20 11:53:23 +08:00
public T Extract<T>()
{
return (T)Extract_Internal(typeof(T));
}
2024-09-08 19:39:43 +08:00
private object Extract_Internal(Type type)//TODO проверить
2024-08-20 11:53:23 +08:00
{
2024-09-08 19:39:43 +08:00
if (_nodes.TryGetValue(type, out InjectionNodeBase node))
2024-08-20 11:53:23 +08:00
{
2024-09-08 19:39:43 +08:00
return node.CurrentInjectedDependencyRaw;
2024-08-20 11:53:23 +08:00
}
2025-03-14 21:57:52 +08:00
throw new InjectionException($"The injection graph is missing a node for {type.Name} type. To create a node, use the Injector.AddNode<{type.Name}>() method directly in the injector or in the implementation of the IInjectionUnit for {type.Name}.");
2024-08-20 11:53:23 +08:00
}
2024-04-28 18:36:14 +08:00
public void AddNode<T>()
{
2024-09-08 19:39:43 +08:00
if (_nodes.ContainsKey(typeof(T)) == false)
2024-04-28 18:36:14 +08:00
{
2024-09-08 19:39:43 +08:00
InitNode(new InjectionNode<T>());
2024-04-28 18:36:14 +08:00
}
}
#endregion
#region Internal
private void InitBranch(InjectionBranch branch)
{
_branches.Add(branch.Type, branch);
foreach (var item in _nodes)
{
var type = item.Key;
var node = item.Value;
if (type.IsAssignableFrom(branch.Type))
{
branch.AddNode(node);
}
}
}
private void InitNode(InjectionNodeBase node)
{
if (_pipeline != null)
{
node.Init(_pipeline);
}
_nodes.Add(node.Type, node);
foreach (var item in _branches)
{
2024-04-28 18:36:14 +08:00
//var type = item.Key;
var branch = item.Value;
2024-03-14 00:24:23 +08:00
if (node.Type.IsAssignableFrom(branch.Type))
{
branch.AddNode(node);
}
}
}
private bool IsCanInstantiated(Type type)
{
return !type.IsAbstract && !type.IsInterface;
}
#endregion
#region Build
private void Init(EcsPipeline pipeline)
{
2024-09-08 19:39:43 +08:00
if (_isInit) { Throw.Exception("Already initialized"); }
_pipeline = pipeline;
2024-09-08 19:39:43 +08:00
foreach (var pair in _nodes)
{
2024-09-08 19:39:43 +08:00
pair.Value.Init(pipeline);
}
_isInit = true;
2024-09-08 19:39:43 +08:00
#if DEBUG
2024-09-08 19:39:43 +08:00
var systems = _pipeline.AllSystems;
var injectType = typeof(IEcsInject<>);
foreach (var system in systems)
{
var type = system.GetType();
foreach (var requiredInjectionType in type.GetInterfaces().Where(o => o.IsGenericType && o.GetGenericTypeDefinition() == injectType).Select(o => o.GenericTypeArguments[0]))
{
_requiredInjectionTypes.Add(requiredInjectionType);
}
}
#endif
}
private bool TryDeclare<T>()
{
Type type = typeof(T);
if (_nodes.ContainsKey(type))
{
return false;
}
2024-09-08 19:39:43 +08:00
InitNode(new InjectionNode<T>());
#if !REFLECTION_DISABLED
if (IsCanInstantiated(type))
#endif
{
2024-04-28 18:36:14 +08:00
InitBranch(new InjectionBranch(this, type));
}
return true;
}
2024-02-25 18:33:17 +08:00
2024-09-07 19:07:47 +08:00
public class Builder : IInjector
{
private EcsPipeline.Builder _source;
private Injector _instance;
private List<InitInjectBase> _initInjections = new List<InitInjectBase>(16);
2024-12-17 12:22:22 +08:00
private EcsWorld _monoWorld;
internal Builder(EcsPipeline.Builder source)
{
_source = source;
_instance = new Injector();
}
2024-02-26 10:37:58 +08:00
public EcsPipeline.Builder AddNode<T>()
{
_instance.TryDeclare<T>();
return _source;
}
2024-02-22 23:48:10 +08:00
public EcsPipeline.Builder Inject<T>(T obj)
{
2025-01-06 10:48:39 +08:00
if (obj is EcsWorld objWorld)
2024-12-17 12:22:22 +08:00
{
2025-01-06 10:48:39 +08:00
if (_monoWorld == null)
2024-12-17 12:22:22 +08:00
{
_monoWorld = objWorld;
}
else
{
Type monoWorldType = _monoWorld.GetType();
Type objWorldType = objWorld.GetType();
if (monoWorldType != objWorldType)
{
2025-01-06 10:48:39 +08:00
if (objWorldType == typeof(EcsWorld))
2024-12-17 12:22:22 +08:00
{ // Екземпляр EcsWorld имеет самый больший приоритет.
_monoWorld = objWorld;
}
2025-01-06 10:48:39 +08:00
if (objWorldType == typeof(EcsDefaultWorld) &&
2024-12-17 12:22:22 +08:00
monoWorldType != typeof(EcsWorld))
{ // Екземпляр EcsDefaultWorld имеет приоритет больше других типов, но меньше приоритета EcsWorld.
_monoWorld = objWorld;
}
}
}
}
_initInjections.Add(new InitInject<T>(obj));
2024-02-22 23:48:10 +08:00
return _source;
}
2024-12-17 12:22:22 +08:00
public EcsPipeline.Builder Extract<T>(ref T obj) // TODO проверить
2024-09-07 19:07:47 +08:00
{
Type type = typeof(T);
for (int i = _initInjections.Count - 1; i >= 0; i--)
{
var item = _initInjections[i];
2024-09-08 19:39:43 +08:00
if (type.IsAssignableFrom(item.Type))
2024-09-07 19:07:47 +08:00
{
obj = (T)item.Raw;
return _source;
}
}
Throw.UndefinedException();
2025-03-14 21:57:52 +08:00
return default;
2024-09-07 19:07:47 +08:00
}
public Injector Build(EcsPipeline pipeline)
{
2024-12-17 12:22:22 +08:00
var monoWorldProcess = pipeline.GetProcess<IMonoWorldInject>(); // TODO Проверить IMonoWorldInject
foreach (var monoWorldSystem in monoWorldProcess)
{
monoWorldSystem.World = _monoWorld;
}
var initInjectionCallbacks = pipeline.GetProcess<IOnInitInjectionComplete>();
foreach (var system in initInjectionCallbacks)
{
system.OnBeforeInitInjection();
}
_instance.Init(pipeline);
foreach (var item in _initInjections)
{
item.InjectTo(_instance);
}
foreach (var system in initInjectionCallbacks)
2024-02-22 23:48:10 +08:00
{
system.OnInitInjectionComplete();
}
return _instance;
}
public void Add(Builder other)
{
foreach (var item in other._initInjections)
{
_initInjections.Add(item);
}
}
2024-09-07 19:07:47 +08:00
void IInjector.Inject<T>(T obj) { Inject(obj); }
T IInjector.Extract<T>()
{
T result = default;
Extract(ref result);
return result;
}
private abstract class InitInjectBase
{
2024-09-07 19:07:47 +08:00
public abstract Type Type { get; }
public abstract object Raw { get; }
public abstract void InjectTo(Injector instance);
}
private sealed class InitInject<T> : InitInjectBase
{
private T _injectedData;
2024-09-07 19:07:47 +08:00
public override Type Type { get { return typeof(T); } }
public override object Raw { get { return _injectedData; } }
public InitInject(T injectedData)
{
_injectedData = injectedData;
}
public override void InjectTo(Injector instance)
{
instance.Inject<T>(_injectedData);
}
}
}
#endregion
}
2024-09-07 19:07:47 +08:00
}