void Awake() { Stopwatch stopwatch = CreateAndStartStopwatch(); sceneInjector = UniInjectUtils.CreateInjector(); UniInjectUtils.SceneInjector = sceneInjector; // Bind the scene injector itself. // This way it can be injected at the scene start // and be used to inject newly created scripts at runtime. sceneInjector.AddBindingForInstance(sceneInjector); // (1) Iterate over scene hierarchy, thereby // (a) find IBinder instances. // (b) find scripts that need injection and how their members should be injected. AnalyzeScene(); // (2) Store bindings in the sceneInjector CreateBindings(); // (3) Inject the bindings from the sceneInjector into the objects that need injection. InjectScriptsThatNeedInjection(); StopAndLogTime(stopwatch, $"SceneInjectionManager - Analyzing, binding and injecting scene took {stopwatch.ElapsedMilliseconds} ms"); // (4) Notify listeners that scene injection has finished foreach (ISceneInjectionFinishedListener listener in sceneInjectionFinishedListeners) { listener.OnSceneInjectionFinished(); } }
private void Inject(object target, object injectionKey) { // For circular dependencies, the object that is currently created for the injectionKey is stored temporarily. // The map is prioritized when resolving dependencies. // Thus, further newly created objects (i.e. dependencies of the result that is constructed here) // that have a dependency to injectionKey (i.e. to the result that is constructed here), // can have the object injected that has already been instantiated here. injectionKeyToObjectWithOngoingInjectionMap.Add(injectionKey, target); // Find all members to be injected via reflection. List <InjectionData> injectionDatas = UniInjectUtils.GetInjectionDatas(target.GetType()); // Inject existing bindings into the fields. foreach (InjectionData injectionData in injectionDatas) { Inject(target, injectionData); } injectionKeyToObjectWithOngoingInjectionMap.Remove(injectionKey); // Notify target that its injection is now finished. if (target is IInjectionFinishedListener) { (target as IInjectionFinishedListener).OnInjectionFinished(); } }
private void InjectMemberFromUnitySearchMethod(MonoBehaviour script, MemberInfo memberInfo, SearchMethods searchMethod, bool isOptional) { Type componentType = ReflectionUtils.GetTypeOfFieldOrProperty(script, memberInfo); // For testing, searching in the scene hierarchy using a Unity method can be simulated to return a mockup for a component. object component = GetComponentFromUnitySearchMethodMockups(script, searchMethod, componentType); if (component == null) { // No mockup found, thus use the real Unity search method. component = UniInjectUtils.InvokeUnitySearchMethod(script, searchMethod, componentType); } if (component != null) { if (memberInfo is FieldInfo) { (memberInfo as FieldInfo).SetValue(script, component); } else if (memberInfo is PropertyInfo) { (memberInfo as PropertyInfo).SetValue(script, component); } else { throw new Exception($"Cannot inject member {script.name}.{memberInfo}." + $" Only Fields and Properties are supported for component injection via Unity methods."); } } else if (!isOptional) { throw new Exception($"Cannot inject member {script.name}.{memberInfo.Name}." + $" No component of type {componentType} found using method {searchMethod}"); } }
private static int CheckInjectableFromUnitySearchMethod(MonoBehaviour script, Type type, InjectionData injectionData) { if (injectionData.InjectionKeys.Length > 1) { // If there are multiple keys, then it must be for a method or constructor with multiple parameters LogErrorCannotBeInjected($"The search method {injectionData.searchMethod} can only be used on a field or property.", script, type, injectionData.MemberInfo); return(1); } object injectionKey = injectionData.InjectionKeys[0]; if (!(injectionKey is Type)) { LogErrorCannotBeInjected($"The search method {injectionData.searchMethod} can not be used with a custom key.", script, type, injectionData.MemberInfo); return(1); } Type componentType = injectionKey as Type; UnityEngine.Object searchResult = UniInjectUtils.InvokeUnitySearchMethod(script, injectionData.searchMethod, componentType); if (searchResult == null) { LogErrorCannotBeInjected($"No instance of {componentType} found using {injectionData.searchMethod}.", script, type, injectionData.MemberInfo); return(1); } return(0); }
private static int CheckInjectable(MonoBehaviour script, Type type, List <IBinding> bindings) { int errorCount = 0; List <InjectionData> injectionDatas = UniInjectUtils.GetInjectionDatas(type); foreach (InjectionData injectionData in injectionDatas) { if (injectionData.isOptional) { continue; } if (injectionData.searchMethod == SearchMethods.SearchInBindings) { errorCount += CheckInjectableFromBindings(script, type, bindings, injectionData); } else { errorCount += CheckInjectableFromUnitySearchMethod(script, type, injectionData); } } return(errorCount); }
internal object[] GetValuesForConstructorInjection(Type type) { if (getValuesForConstructorInjectionVisitedTypes.Contains(type)) { throw new CyclicConstructorDependenciesException($"Circular dependencies in the constructor parameters of type {type}"); } getValuesForConstructorInjectionVisitedTypes.Add(type); ConstructorInjectionData constructorInjectionData = UniInjectUtils.GetConstructorInjectionData(type); object[] injectionKeys = constructorInjectionData.InjectionKeys; object[] result = GetValuesForInjectionKeys(injectionKeys); getValuesForConstructorInjectionVisitedTypes.Remove(type); return(result); }
private void AnalyzeScriptsRecursively(GameObject gameObject) { MonoBehaviour[] scripts = gameObject.GetComponents <MonoBehaviour>(); foreach (MonoBehaviour script in scripts) { // The script can be null if it is a missing component. if (script == null) { continue; } // Analyzing a type for InjectionData is costly. // The types of the UnityEngine do not make use of UniInject. // Thus, the scripts from the UnityEngine itself should be skipped for better performance. Type type = script.GetType(); if (!string.IsNullOrEmpty(type.Namespace) && type.Namespace.StartsWith("UnityEngine.")) { continue; } if (script is IBinder) { binders.Add(script as IBinder); } if (script is ISceneInjectionFinishedListener) { sceneInjectionFinishedListeners.Add(script as ISceneInjectionFinishedListener); } if ((!onlyInjectScriptsWithMarkerInterface || script is INeedInjection) && !(script is IExcludeFromSceneInjection)) { List <InjectionData> injectionDatas = UniInjectUtils.GetInjectionDatas(script.GetType()); if (injectionDatas.Count > 0) { scriptsThatNeedInjection.Add(script); } } } foreach (Transform child in gameObject.transform) { AnalyzeScriptsRecursively(child.gameObject); } }
public void DoInjection() { Stopwatch stopwatch = CreateAndStartStopwatch(); sceneInjector = UniInjectUtils.CreateInjector(); UniInjectUtils.SceneInjector = sceneInjector; // Try to find a UIDocument GameObject uiDocumentGameObject = GameObject.FindGameObjectWithTag("UIDocument"); if (uiDocumentGameObject != null) { UIDocument uiDocument = uiDocumentGameObject.GetComponent <UIDocument>(); if (uiDocument != null) { sceneInjector.RootVisualElement = uiDocument.rootVisualElement; } } // Bind the scene injector itself. // This way it can be injected at the scene start // and be used to inject newly created scripts at runtime. sceneInjector.AddBindingForInstance(sceneInjector); // (1) Iterate over scene hierarchy, thereby // (a) find IBinder instances. // (b) find scripts that need injection and how their members should be injected. AnalyzeScene(); // (2) Store bindings in the sceneInjector CreateBindings(); // (3) Inject the bindings from the sceneInjector into the objects that need injection. InjectScriptsThatNeedInjection(); StopAndLogTime(stopwatch, $"SceneInjectionManager - Analyzing, binding and injecting scene took {stopwatch.ElapsedMilliseconds} ms"); // (4) Notify listeners that scene injection has finished foreach (ISceneInjectionFinishedListener listener in sceneInjectionFinishedListeners) { listener.OnSceneInjectionFinished(); } }