Last active
February 15, 2024 04:59
-
-
Save instance-id/e98ddf640e71449e84109f72fffa5fd9 to your computer and use it in GitHub Desktop.
OneShot - Dependency Injection: Example Registration and Injection Handler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using instance.id.DataTypes; | |
using instance.id.Logging; | |
using UnityEngine; | |
using UnityEngine.SceneManagement; | |
using UnityObject = UnityEngine.Object; | |
#if UNITY_EDITOR | |
using UnityEditor; | |
#endif | |
namespace instance.id | |
{ | |
[RelativePath("instance.id", "DependencyInjection")] | |
[CreateAssetMenu(fileName = "DIContainer.asset", menuName = "instance.id/DI Container", order = 1)] | |
public class DIContainer : AssetInstance<DIContainer> | |
{ | |
public Container Container { get; } = new Container(); | |
#if !UNITY_EDITOR | |
private void Awake() => SetupDIContainer(); | |
#endif | |
#if UNITY_EDITOR | |
private void OnEnable() => RunInjection(); | |
[InitializeOnLoadMethod] | |
#endif | |
public static void SetupDIContainer() => RegistrationHandler(); | |
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] | |
private static void RunInjection() => I.InjectionHandler(); | |
// --| Registration Receiver -------------------------------- | |
// --| ------------------------------------------------------ | |
public ResolverBuilder Register<T>(T instance) => | |
I.Container.RegisterInstance<T>(instance); | |
// --| DI Registration Initializer -------------------------- | |
// --| ------------------------------------------------------ | |
private static void RegistrationHandler() | |
{ | |
var initializers = new List<MethodInfo>(); | |
#if UNITY_EDITOR | |
try | |
{ | |
EditorApplication.update += EditorUpdate; | |
// --| Editor Only TypeCache ------------------------ | |
initializers = TypeCache.GetMethodsWithAttribute<DIInitializer>().ToList(); | |
#else | |
// --| Runtime Type/Method Cache -------------------- | |
initializers = RuntimeMethodCache.GetMethodsWithAttribute<DIInitializer>().ToList(); | |
#endif | |
if (initializers.Count == 0) | |
{ | |
StaticLog("No DIInitializer Attributes found.", Level.Warning, Channel.EditorDebug); | |
return; | |
} | |
for (var i = 0; i < initializers.Count; i++) | |
{ | |
var mInfo = initializers[i]; | |
// --| Search for loaded instances -------------- | |
var instances = FindObjectsOfType(mInfo.DeclaringType).ToList(); | |
// --| Editor/AssetDatabase, else/Resources ----- | |
if (instances.Count == 0) | |
instances = FindAssets<UnityObject>(mInfo.DeclaringType); | |
if (instances.Count == 0) | |
{ | |
StaticLog($"No instances of {mInfo.DeclaringType?.Name} found.", Level.Warning, Channel.EditorDebug); | |
continue; | |
} | |
// --| Invoke DI Registration Initializer ------- | |
for (var j = 0; j < instances.Count; j++) | |
mInfo.Invoke(instances[j], null); | |
} | |
#if UNITY_EDITOR | |
} | |
finally | |
{ | |
EditorApplication.update -= EditorUpdate; | |
StaticLog("DI Registration: Complete.", Level.Debug, Channel.EditorDebug); | |
} | |
#endif | |
} | |
// --| DI Injection Handler --------------------------------- | |
// --| ------------------------------------------------------ | |
public void InjectionHandler() | |
{ | |
var injectable = new List<Type>(); | |
#if UNITY_EDITOR | |
try | |
{ | |
EditorApplication.update += EditorUpdate; | |
injectable = TypeCache.GetTypesWithAttribute<InjectableAttribute>().ToList(); | |
#else | |
injectable = RuntimeTypeCache.GetTypesWithAttribute<InjectableAttribute>().ToList(); | |
#endif | |
for (var i = 0; i < injectable.Count; i++) | |
{ | |
var instances = FindObjectsOfType(injectable[i]).ToList(); | |
if (instances.Count == 0) continue; | |
for (var j = 0; j < instances.Count; j++) | |
Container.InjectAll(instances[j], instances[j].GetType()); | |
} | |
#if UNITY_EDITOR | |
} | |
finally | |
{ | |
EditorApplication.update -= EditorUpdate; | |
Log("DI Injection: Complete.", Level.Debug, Channel.EditorDebug); | |
} | |
#endif | |
} | |
// --| Scene Injection Performance Comparison --------------- | |
// --| ------------------------------------------------------ | |
public void SceneInjection() | |
{ | |
var injectable = new List<Type>(); | |
#if UNITY_EDITOR | |
try | |
{ | |
EditorApplication.update += EditorUpdate; | |
var testName = "SceneInjection"; | |
Timer timer = new Timer("SceneSearch_Injection", typeof(DIContainer), true); | |
timer.Toggle(testName); | |
injectable = TypeCache.GetTypesWithAttribute<InjectableAttribute>().ToList(); | |
#else | |
injectable = RuntimeTypeCache.GetTypesWithAttribute<InjectableAttribute>().ToList(); | |
#endif | |
var objects = SceneManager.GetActiveScene().GetRootGameObjects(); | |
for (var i = 0; i < objects.Length; i++) | |
{ | |
var components = objects[i].GetComponents<MonoBehaviour>(); | |
for (var j = 0; j < components.Length; j++) | |
{ | |
if (!injectable.Contains(components[j].GetType())) continue; | |
Container.InjectAll(components[j], components[j].GetType()); | |
#if UNITY_EDITOR | |
Log($"Injecting {components[j].name}", Level.Debug, Channel.EditorDebug); | |
#endif | |
} | |
} | |
#if UNITY_EDITOR | |
timer.Toggle(testName); | |
timer.PrintAll(Timer.DisplayType.Console); | |
} | |
finally | |
{ | |
Log("Scene Injection Process: Complete", Level.Debug, Channel.EditorDebug); | |
EditorApplication.update -= EditorUpdate; | |
} | |
#endif | |
} | |
#if UNITY_EDITOR | |
// --| Editor Update Loop ------------------------- | |
static void EditorUpdate() => EditorApplication.QueuePlayerLoopUpdate(); | |
#endif | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using instance.id.DataTypes; | |
using instance.id.Validator; | |
using UnityEngine; | |
namespace instance.id | |
{ | |
[AttributeUsage(AttributeTargets.Class)] | |
public class InjectableAttribute : Attribute { } | |
[Injectable] | |
[Serializable] [ExecuteInEditMode] | |
public class InjectionTest : MonoBehaviour | |
{ | |
[Inject] | |
public LoadingTrigger injectTest01; | |
[SerializeField] | |
private GameObject injectTest02; | |
public GameObject LoadingTrigger | |
{ | |
get => injectTest02; | |
set => injectTest02 = value; | |
} | |
[Inject] [SerializeField] | |
private SectionCollection injectTest03; | |
[Inject] | |
internal void Inject(LoadingTrigger loadTrigger) | |
{ | |
Debug.Log($"Injecting {loadTrigger.name}"); | |
LoadingTrigger = loadTrigger.gameObject; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using instance.id.Collections; | |
using instance.id.DataTypes; | |
using UnityEngine; | |
namespace instance.id | |
{ | |
[AttributeUsage(AttributeTargets.Method)] | |
public class DIInitializer : Attribute { } | |
[Serializable] | |
[CreateAssetMenu(menuName = "instance.id/Collections/Create SectionCollection", fileName = "SectionCollection", order = 0)] | |
public class SectionCollection : ScriptableObjectCollection<Section> | |
{ | |
[DIInitializer] | |
public void RegisterDI() | |
{ | |
id.di.RegisterInstance<SectionCollection>(CollectionsRegistry.Instance.TryGetCollectionOfType<SectionCollection>()).AsSelf(); | |
#if UNITY_EDITOR | |
Debug.Log($"RegisterDI Invoked on {nameof(SectionCollection)}"); | |
#endif | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment