intentor / adic Goto Github PK
View Code? Open in Web Editor NEWLightweight dependency injection container for Unity
License: Other
Lightweight dependency injection container for Unity
License: Other
Create a more detailed factory example, showing how to instantiate multiple game objects from a single prefab with custom properties (color, location, name, etc.).
Maybe the example could instantiate many game objects in a matrix like structure and configure their properties individually using some internal state the factory receives as dependency.
Inject parameters on PostConstruct
methods just like a constructor would be.
To emulate the functionality of MonoBehaviour.Invoke()
for commands, the Dispatch()
method could receive an overload to set an invocation time.
It would be convenient to be able to use Enums in addition to strings as identifiers
Add a ToGameObjectsWithTag()
method to the Unity Binding Extension.
The idea is that this method adds as many game objects as found by GameObject.FindGameObjectsWithTag()
.
Any conditions must be bound to all of the found objects.
Currently, injection by a single container in a multicontainer structure is only available to MonoBehviours through extension.
The idea is that this functionality should be available for the entire framework.
When trying to inject a IEnumerable, the following exception occurs:
InjectorException: There are no constructors on the type System.Collections.Generic.IEnumerable`1[System.Int32].
Adic.Injection.Injector.Instantiate (System.Type type) (at Assets/Adic/Scripts/Framework/Injection/Injector.cs:455)
Adic.Injection.Injector.Resolve (System.Type type, InjectionMember member, System.Object parentInstance, System.Object identifier) (at Assets/Adic/Scripts/Framework/Injection/Injector.cs:227)
Adic.Injection.Injector.InjectFields (System.Object instance, Adic.Cache.SetterInfo[] fields) (at Assets/Adic/Scripts/Framework/Injection/Injector.cs:328)
Adic.Injection.Injector.Inject (System.Object instance, Adic.Cache.ReflectedClass reflectedClass) (at Assets/Adic/Scripts/Framework/Injection/Injector.cs:301)
Adic.Injection.Injector.Instantiate (System.Type type) (at Assets/Adic/Scripts/Framework/Injection/Injector.cs:465)
Adic.Injection.Injector.ResolveBinding (Adic.Binding.BindingInfo binding, System.Type type, InjectionMember member, System.Object parentInstance, System.Object identifier) (at Assets/Adic/Scripts/Framework/Injection/Injector.cs:415)
Adic.Injection.Injector.Resolve (System.Type type, InjectionMember member, System.Object parentInstance, System.Object identifier) (at Assets/Adic/Scripts/Framework/Injection/Injector.cs:232)
Adic.Injection.Injector.ResolveHelloWorld (at Assets/Adic/Scripts/Framework/Injection/Injector.cs:51)
Adic.Examples.HelloWorld.GameRoot.SetupContainers () (at Assets/Adic/Examples/01_HelloWorld/Scripts/GameRoot.cs:18)
Adic.ContextRoot.Awake () (at Assets/Adic/Scripts/Extensions/ContextRoot/ContextRoot.cs:52)
When building to iOS, the following error occurs with Adic 2.9:
ExecutionEngineException: Attempting to JIT compile method '(wrapper dynamic-method) Adic.InjectionContainer:InjectionContainer ()' while running with --aot-only.
at System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) [0x00000] in <filename unknown>:0
at System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) [0x00000] in <filename unknown>:0
at System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method) [0x00000] in <filename unknown>:0
at System.Reflection.Emit.DynamicMethod.CreateDelegate (System.Type delegateType) [0x00000] in <filename unknown>:0
at Adic.Util.MethodUtils.CreateConstructor (System.Type type, System.Reflection.ConstructorInfo constructor) [0x00000] in <filename unknown>:0
at Adic.Cache.ReflectionFactory.Create (System.Type type) [0x00000] in <filename unknown>:0
at Adic.Cache.ReflectionCache.Add (System.Type type) [0x00000] in <filename unknown>:0
at Adic.Cache.ReflectionCache.GetClass (System.Type type) [0x00000] in <filename unknown>:0
at Adic.Injection.Injector.Inject (System.Object instance) [0x00000] in <filename unknown>:0
at Adic.Injection.Injector.OnBeforeAddBinding (IBinder source, Adic.Binding.BindingInfo& binding) [0x00000] in <filename unknown>:0
at Adic.Binding.Binder.AddBinding (Adic.Binding.BindingInfo binding) [0x00000] in <filename unknown>:0
at Adic.Binding.BindingFactory.AddBinding (System.Object value, BindingInstance instanceType) [0x00000] in <filename unknown>:0
at Adic.Binding.BindingFactory.To (System.Type type, System.Object instance) [0x00000] in <filename unknown>:0
at Adic.Binding.BindingFactory.To[InjectionContainer] (Adic.InjectionContainer instance) [0x00000] in <filename unknown>:0
at Adic.InjectionContainer.RegisterItself () [0x00000] in <filename unknown>:0
at Adic.InjectionContainer..ctor () [0x00000] in <filename unknown>:0
at MastersOfConquest.GameRoot.SetupContainers () [0x00000] in <filename unknown>:0
at Adic.ContextRoot.Awake () [0x00000] in <filename unknown>:0
Using the 2.7 version works fine.
Issue happens on iPhone 6, iOS 8.3.
Generate DLLs for framework and extensions, so they can be used outside Unity.
The documentation should also be updated to explain the uses of the DLL and also the fact that the extensions won't work outside Unity.
The command behaviours are not correctly providing the methods to be called from the Unity UI.
It would be great if I could get to the name that is trying to be resolved in the IFactory create method.
Thanks,
Gregg
A factory can create different types related to the same type, so not necessarily a factory creates a single type. So, this property doesn't always indicate the correct type being created by the factory - and it should not be a restriction for anyone using factories.
Also, given the framework doesn't use the factoryType property in all situations when dealing with factories, it's better to remove the property.
The removal of the property won't cause problems to any existing factory - it'll be safe to remove it from any existing code.
Create strongly types commands, that could also be used through the Unity UI (e.g. by passing a parameter from a input value).
The declaration of the command can have the type of its parameter.
Consider the following scenario:
To prevent having to recreate all bindings from 1, it should be interesting to have some form of subcontainer in which 2 and 3 would inherit all bindings from 1.
During regular development, the use of non singleton commands have been very restricted, usually to commands that rely on some asynchronous call.
This way, to prevent having to always identify a command as singleton, make this option as the default for every command.
When manually resolving an object that has conditions from WhenInto(), a null reference exception is raised because InjectionContext.parentInstance is null.
I am making spaceship game which currently doesn't use DI, but have read about this framework and would like to use it. Some basic info about my game:
I was thinking about making each ship a context - that way I could inject ship configuration, player input etc. to the ship's submodules (which are sub transforms of the ship). The ship may be nested when docked.
This does however not seem to be entirely achievable using ADIC.
How would you use ADIC in this case?
The idea is to create a binding action that binds to all interfaces of a given type, like:
container.BindToInterfaces<MyClass>().ToSingleton<MyClass>()
could be done.The BindToInterfaces<T>()
could receive classes or interfaces.
It could be also created a method that bounds to all inherited and implemented types of a given type.
As of Adic v2.19 the code below throws a "InvalidCastException: Cannot cast from source type to destination type." exception. Adic v2.18 does not throw this exception. I'm able to fix the problem by changing "ToNamespace" to "ToNamespaceSingleton" but I don't actually want the classes derived by ITestInterface to be singletons, just the parent class.
using System;
using Adic;
public class ToNamespaceTest : ContextRoot {
public override void SetupContainers() {
var container = this.AddContainer<InjectionContainer>();
container.RegisterExtension<EventCallerContainerExtension>();
container.Bind<ITestInterface>().ToNamespace("Test");
container.Bind<TestParent>().ToSingleton();
}
public override void Init() {}
}
public interface ITestInterface {}
public class TestParent {
[Inject] public ITestInterface Children;
}
namespace Test {
public class TestChild1 : ITestInterface, IDisposable {
public void Dispose() {}
}
}
PostConstruct is being called twice.
And example of the problem can be read at forum.unity3d.com/threads/released-adic-dependency-injection-container.311895/#post-2324897.
Execute automatic injection on any MonoBehaviours on the current scene.
Also add an option, selectable through GameRoot, that allows injection only on objects of a given type (the base behaviour).
Add an Unbind()
method to the container, so it's possible to unbind a type.
It's also important to think on how to unbind types that are related to an identifier, because if just types can be unbound it can lead to the removal of bindings with identifiers.
Editor script of ContextRoot.cs hides all public or [SerializeField] fields in Inspector in child scripts. For example: we'd like to instantiate array of gameobject in Init() method. But we can't associate prefabs with array in inspector. Version of Adic: 2.16.1
Hi,
we had problems troubleshooting injection issues when requesting bindings with components.
Unity did throw some warning, but the stack made it hard to troubleshoot:
You are trying to create a MonoBehaviour using the 'new' keyword. [...]
An error should be implemented to help users pin-point the issue fast. The issue can happen often when using injection with components, either when component values are null or when the binding order is wrong.
I managed to implement an error message that greatly helps finding from which components are problematic:
josimard@21cab92
Ultimately, we did not find how to specify the injector not to instantiate some types... It was hard understand why we had multiple instances of our behaviors when using injection at first. We are not sure why the binding order is so important in our case...
This is how we bind component instances in our container:
container.Bind<T>().To(instance);
Add note on documentation about script execution order, pointing that it's interesting to allow the GameRoot to execute before any other scripts on the game.
While commands propose a method for tackling the messages/events problem, how are Adic commands different from say injecting an arbitrary object and executing one of its methods? Thanks!
Every singleton instance that shares the same type (but not the same key) should be the same instance.
Currently if a binding is created with different keys but for different types, new singleton instances will be created.
You are doing a lot of nice work on this framework. I had one suggestion that I have liked in some DI frameworks. Can you add chaining to the IInjectionContainer? Just have each method return the container? So the code below:
container.Bind<IAudioSettings>().To(settingsManager.Get<AudioSettings>("Audio"));
container.Bind<ISocketManager>().ToSingleton<SocketManager>();
container.Bind<IAudioManager>().ToSingleton<AudioManager>();
Would become:
container
.Bind<IAudioSettings>().To(settingsManager.Get<AudioSettings>("Audio"))
.Bind<ISocketManager>().ToSingleton<SocketManager>()
.Bind<IAudioManager>().ToSingleton<AudioManager>();
There should be a method to add bindings setup from namespace, making it easy to add a large amout of binding classes.
Add manual resolution by identifier.
We have been using your framework extensively, and it has been working very well for us. Some members of my team have been declaring [Inject] on private or protected properties to keep their public interface only showing what they want. However, we upgraded to the latest framework, and all of those now throw a null reference because the framework cannot find a public setter. What are your thoughts on this? Is it appropriate to have private or protected properties because they are only used for injection? Would this affect performance if it was allowed? It would be great if we could allow this since we already have so much code written with it.
Here is the code we would need to change if we wanted to allow private and protected properties to be injected. Just put true on propertyInfo.GetSetMethod()
.
MethodUtils.cs
public static Setter CreatePropertySetter(Type type, PropertyInfo propertyInfo) {
#if UNITY_IOS
return (object instance, object value) => propertyInfo.SetValue(instance, value, null);
#else
var propertySetMethod = propertyInfo.GetSetMethod(true);
var parametersTypes = new Type[] { OBJECT_TYPE, OBJECT_TYPE };
DynamicMethod method = new DynamicMethod(propertyInfo.Name, typeof(void), parametersTypes, true);
ILGenerator generator = method.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Callvirt, propertySetMethod);
generator.Emit(OpCodes.Ret);
return (Setter)method.CreateDelegate(typeof(Setter));
#endif
}
Thanks,
Gregg
If it's not possible to instantiate a given type, return null.
That should be a mode of Adic, so the current behaviour (always instantiate) continue to exist.
Related to #45.
To allow the use of named constants on constructor parameters, the Inject
attribute should also work on them.
To better organize the docs, split its various parts on the wiki, keeping on the landing page only the basics about the framework.
The documentation should be updated with information about what DI is, why it's important and some use cases that exemplify that importance.
To improve compilation times, Adic's folder should be moved to the Plugins folder.
There should be an overload to ICommandDispatcher.Release()
that releases all commands of given type that are running.
To prevent misunderstandings about how Adic track prefabs, a new section should be added explaining that, aside from singleton instances, once a object is resolved by the container it's not tracked by it.
Check for any compilation errors on Windows Phone/Apps.
When unbinding a type that is singleton, all types bound to it should be also unbound.
Improve documentation about the using of command behaviours on Using commands, maybe providing examples with the Unity UI.
From Carlos Gimenez:
When trying to inject the same instance of a simple class by binding it as Singleton, seems it calls his constructor and PostConstructor every time its injected. So in classes that I wanted to be initialized only once, like SettingsData, UserData and so I had to add an static variable to check if it already loaded the data for example.
Update documentation with a real world use case for games using Adic.
Using the Inject
attribute, it should be possible to inject on methods:
[Inject]
public void Method(TypeA paramA, TypeB paramB) {
...
}
When a command that has a coroutine is released, its coroutine(s) should be stopped.
Factories should be created by the container, allowing them to receive dependencies by constructor/field/properties.
When dispatching, return the dispatched command, so it can e.g. be released manually.
I have worked with other IOC frameworks in C#, and the one you have built for Unity is fantastic. Definitely, the easiest to use and make the most sense. Thank you.
Can you tell me what the lifetime of something created by a factory is, is it a singleton, or created per resolve?
Thanks again,
Gregg
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.