com.bckworks.zenject
v1.1.2
Published
Dependency Injection Framework for Unity3D
Downloads
2
Readme
COMMITMENT TO OPEN SOURCE
We are proud to provide this useful tool to the community and encourage those developing projects to fork from it. To date Zenject has been forked Over 300 times and we love to see the benefit it is deriving for users and support this!
Modest Tree’s Zenject is open-sourced in order to support the community and encourage collaborative development. This is clearly licensed with an MIT license to allow a high degree of flexibility and freedom of use.
We look forward to hearing about the cool projects you are building with Zenject and supporting its use! Contact us through github or [email protected].
Upcoming Update
As our Team is looking at Improving and Supporting Zenject, Below is the list of Features and Changes we are planning on applying to Zenject.
- move all pooling functionality into the factories
- add Free() to all IFactory types and have the default Free() dispose
- change pooling to allow a pooled object to be returned to pool via PoolFactory.Free()
- add a PoolHandle struct and IFactory.CreateHandle(), disposing the handle returns object to factory
- eliminate all pooling dependancies from the types being pooled
- add additional install functions to control pooling via installers rather than by explicit code
- PRO: when using PoolHandles, pooling or non-pooling is completely transparent to the consuming types
- CON: breaking changes with existing pooling techniques
- move unity-specific handling out of zenject core and into an additional library
- initially zenject was unity-only but now it is now used in standalone .EXE as well
- zenject codebase shows many scars from when it was unity-only
- eliminate all the #IFDEFs and move unity-specifics to external package
- allow building zenject core to DLL, unity-specifics stay as c# in unity project
- PRO: reduces compile times in unity
- PRO: more usable outside unity, fewer unity-landmines to step on
- PRO: far less zenject code to step through when debugging user-code
- CON: may require manual updating of some unity scenes and prefabs
add dependancy graph resolve support
- somebody first needs to understand how zenject stores its bindings. I currently do not.
- we wana dump all the bindings for a set of scenes/installers to xml or something
- an external python or winforms tool allows viewing the binding graph
- show each container as a node, bindings are child nodes, draw dependancy links between bindings,this will make finding unnecessary bindings and fixing binding errors a lot easier,eventually we can also add a binding editor and error highlighting for bad bindings
- PRO: quality-of-life and general workflow improvement
- CON: additional software to maintain
We are sharing this with community to help us prioritize and Find out if there is anything else that the community is looking for in the upcoming release. Looking forward to your feedback - Contact us through github or [email protected].
Dependency Injection Framework for Unity3D
Introduction
Zenject is a lightweight highly performant dependency injection framework built specifically to target Unity 3D (however it can be used outside of Unity as well). It can be used to turn your application into a collection of loosely-coupled parts with highly segmented responsibilities. Zenject can then glue the parts together in many different configurations to allow you to easily write, re-use, refactor and test your code in a scalable and extremely flexible way.
Tested in Unity 3D on the following platforms:
- PC/Mac/Linux
- iOS
- Android
- WebGL
- PS4 (with IL2CPP backend)
- Windows Store (including 8.1, Phone 8.1, Universal 8.1 and Universal 10 - both .NET and IL2CPP backend)
IL2CPP is supported, however there are some gotchas - see here for details
This project is open source. You can find the official repository here.
For general troubleshooting / support, please post to stack overflow using the tag 'zenject', or post in the zenject google group
Or, if you have found a bug, you are also welcome to create an issue on the github page, or a pull request if you have a fix / extension. There is also a gitter chat that you can join for real time discussion. You can also follow @Zenject on twitter for updates.
Features
- Injection
- Supports both normal C# classes and MonoBehaviours
- Constructor injection
- Field injection
- Property injection
- Method injection
- Conditional binding (eg. by type, by name, etc.)
- Optional dependencies
- Support for creating objects after initialization using factories
- Nested Containers aka Sub-Containers
- Injection across different Unity scenes to pass information from one scene to the next
- Scene parenting, to allow one scene to inherit the bindings from another
- Support for global, project-wide bindings to add dependencies for all scenes
- Convention based binding, based on class name, namespace, or any other criteria
- Ability to validate object graphs at editor time (including dynamic object graphs created via factories)
- Automatic binding on components in the scene using the
ZenjectBinding
component - Auto-Mocking using the Moq library
- Built-in support for memory pools
- Support for decorator pattern using decorator bindings
- Support for automatically mapping open generic types
- Built in support for unit test, integration tests, and scene tests
- Just-in-time injection using the LazyInject<> construct
- Support for multiple threads for resolving/instantiating
- Support for 'reflection baking' to eliminate costly reflection operations completely by directly modifying the generated assemblies
- Automatic injection of game objects using ZenAutoInjecter component
Installation
You can install Zenject using any of the following methods
From Releases Page. Here you can choose between the following:
- Zenject-WithAsteroidsDemo.vX.X.unitypackage - This is equivalent to what you find in the Asset Store and contains both sample games "Asteroids" and "SpaceFighter" as part of the package. All the source code for Zenject is included here.
- Zenject.vX.X.unitypackage - Same as above except without the Sample projects.
- Zenject-NonUnity.vX.X.zip - Use this if you want to use Zenject outside of Unity (eg. just as a normal C# project)
From the Asset Store Page
- Normally this should be the same as what you find in the Releases section, but may also be slightly out of date since Asset Store can take a week or so to review submissions sometimes.
From Source
- After syncing the git repo, note that you will have to build the
Zenject-Usage.dll
by building the solution atAssemblyBuild\Zenject-usage\Zenject-usage.sln
. Or, if you prefer you can getZenject-Usage.dll
from Releases section instead - Then you can copy the
UnityProject/Assets/Plugins/Zenject
directory to your own Unity3D project.
- After syncing the git repo, note that you will have to build the
Note that when importing Zenject into your unity project, you can uncheck any folder underneath the OptionalExtras folder for cases where you don't want to include it, or if you just want the core zenject functionality, you can uncheck the entire OptionalExtras directory.
History
Unity is a fantastic game engine, however the approach that new developers are encouraged to take does not lend itself well to writing large, flexible, or scalable code bases. In particular, the default way that Unity manages dependencies between different game components can often be awkward and error prone.
This project was started because at the time there wasn't any DI frameworks for Unity, and having used DI frameworks outside of Unity (eg. Ninject) and seeing the benefits, I felt it was important to remedy that.
Finally, I will just say that if you don't have experience with DI frameworks, and are writing object oriented code, then trust me, you will thank me later! Once you learn how to write properly loosely coupled code using DI, there is simply no going back.
Documentation
The Zenject documentation is split up into the following sections. It is split up into two parts (Introduction and Advanced) so that you can get up and running as quickly as possible. I would recommend at least skimming through the Introduction section before beginning, but then feel free to jump around in the advanced section as necessary
Another great starting point is to watch this youtube series on zenject created by Infallible Code.
You might also benefit from playing with the provided sample projects (which you can find by opening Zenject/OptionalExtras/SampleGame1
or Zenject/OptionalExtras/SampleGame2
).
If you are a DI veteran, then it might be worth taking a look at the cheatsheet at the bottom of this page, which should give you an idea of the syntax, which might be all you need to get started.
The tests may also be helpful to show usage for each specific feature (which you can find at Zenject/OptionalExtras/UnitTests
and Zenject/OptionalExtras/IntegrationTests
)
Also see further reading section for some external zenject tutorials provided elsewhere.
Table Of Contents
- Introduction
- What is Dependency Injection?
- Theory
- Misconceptions
- Zenject API
- Hello World Example
- Injection
- Binding
- Binding
- Construction Methods
- Installers
- Using Non-MonoBehaviour Classes
- ITickable
- IInitializable
- IDisposable
- BindInterfacesTo and BindInterfacesAndSelfTo
- Using the Unity Inspector To Configure Settings
- Object Graph Validation
- Scene Bindings
- General Guidelines / Recommendations / Gotchas / Tips and Tricks
- Further Reading
- What is Dependency Injection?
- Advanced
- Binding
- Game Object Bind Methods
- Optional Binding
- Conditional Bindings
- List Bindings
- Global Bindings Using Project Context
- Identifiers
- Non Generic bindings
- Convention Based Binding
- Scriptable Object Installer
- Runtime Parameters For Installers
- Creating Objects Dynamically Using Factories
- Memory Pools
- Update / Initialization Order
- Zenject Order Of Operations
- Injecting data across scenes
- Scene Parenting Using Contract Names
- Just-In-Time Resolving Using LazyInject<>
- Scene Decorators
- ZenAutoInjecter
- Sub-Containers And Facades
- Writing Automated Unit Tests / Integration Tests
- Philosophy Of Zenject
- Using Zenject Outside Unity Or For DLLs
- Zenject Settings
- Signals
- Decorator Bindings
- Open Generic Types
- Notes About Destruction/Dispose Order
- UniRx Integration
- Auto-Mocking using Moq
- Creating Unity EditorWindow's with Zenject
- Optimization Recommendations/Notes
- Reflection Baking
- Upgrade Guide for Zenject 6
- DiContainer Methods
- DiContainer.Instantiate
- DiContainer.Bind
- DiContainer.Resolve
- DiContainer.Inject
- DiContainer.QueueForInject
- DiContainer Unbind / Rebind
- Other DiContainer methods
- Binding
- Frequently Asked Questions
- Isn't this overkill? I mean, is using statically accessible singletons really that bad?
- Is there a way to integrate with the upcoming Unity ECS?
- Does this work on AOT platforms such as iOS and WebGL?
- How is Performance?
- Does Zenject support multithreading?
- Are there any more sample projects with source to look at?
- What games/tools/libraries are using Zenject
- I keep getting errors complaining about circular reference! How to address this?
- Cheat Sheet
- Further Help
- Release Notes
- License
Theory
What follows is a general overview of Dependency Injection from my perspective. However, it is kept light, so I highly recommend seeking other resources for more information on the subject, as there are many other people (often with better writing ability) that have written about the theory behind it.
When writing an individual class to achieve some functionality, it will likely need to interact with other classes in the system to achieve its goals. One way to do this is to have the class itself create its dependencies, by calling concrete constructors:
public class Foo
{
ISomeService _service;
public Foo()
{
_service = new SomeService();
}
public void DoSomething()
{
_service.PerformTask();
...
}
}
This works fine for small projects, but as your project grows it starts to get unwieldy. The class Foo is tightly coupled to class 'SomeService'. If we decide later that we want to use a different concrete implementation then we have to go back into the Foo class to change it.
After thinking about this, often you come to the realization that ultimately, Foo shouldn't bother itself with the details of choosing the specific implementation of the service. All Foo should care about is fulfilling its own specific responsibilities. As long as the service fulfills the abstract interface required by Foo, Foo is happy. Our class then becomes:
public class Foo
{
ISomeService _service;
public Foo(ISomeService service)
{
_service = service;
}
public void DoSomething()
{
_service.PerformTask();
...
}
}
This is better, but now whatever class is creating Foo (let's call it Bar) has the problem of filling in Foo's extra dependencies:
public class Bar
{
public void DoSomething()
{
var foo = new Foo(new SomeService());
foo.DoSomething();
...
}
}
And class Bar probably also doesn't really care about what specific implementation of SomeService Foo uses. Therefore we push the dependency up again:
public class Bar
{
ISomeService _service;
public Bar(ISomeService service)
{
_service = service;
}
public void DoSomething()
{
var foo = new Foo(_service);
foo.DoSomething();
...
}
}
So we find that it is useful to push the responsibility of deciding which specific implementations of which classes to use further and further up in the 'object graph' of the application. Taking this to an extreme, we arrive at the entry point of the application, at which point all dependencies must be satisfied before things start. The dependency injection lingo for this part of the application is called the 'composition root'. It would normally look like this:
var service = new SomeService();
var foo = new Foo(service);
var bar = new Bar(service);
var qux = new Qux(bar);
.. etc.
DI frameworks such as Zenject simply help automate this process of creating and handing out all these concrete dependencies, so that you don't need to explicitly do so yourself like in the above code.
Misconceptions
There are many misconceptions about DI, due to the fact that it can be tricky to fully wrap your head around at first. It will take time and experience before it fully 'clicks'.
As shown in the above example, DI can be used to easily swap different implementations of a given interface (in the example this was ISomeService). However, this is only one of many benefits that DI offers.
More important than that is the fact that using a dependency injection framework like Zenject allows you to more easily follow the 'Single Responsibility Principle'. By letting Zenject worry about wiring up the classes, the classes themselves can just focus on fulfilling their specific responsibilities.
Another common mistake that people new to DI make is that they extract interfaces from every class, and use those interfaces everywhere instead of using the class directly. The goal is to make code more loosely coupled, so it's reasonable to think that being bound to an interface is better than being bound to a concrete class. However, in most cases the various responsibilities of an application have single, specific classes implementing them, so using interfaces in these cases just adds unnecessary maintenance overhead. Also, concrete classes already have an interface defined by their public members. A good rule of thumb instead is to only create interfaces when the class has more than one implementation or in cases where you intend to have multiple implemenations in the future (this is known, by the way, as the Reused Abstraction Principle)
Other benefits include:
- Refactorability - When code is loosely coupled, as is the case when using DI properly, the entire code base is much more resilient to changes. You can completely change parts of the code base without having those changes wreak havoc on other parts.
- Encourages modular code - When using a DI framework you will naturally follow better design practices, because it forces you to think about the interfaces between classes.
- Testability - Writing automated unit tests or user-driven tests becomes very easy, because it is just a matter of writing a different 'composition root' which wires up the dependencies in a different way. Want to only test one subsystem? Simply create a new composition root. Zenject also has some support for avoiding code duplication in the composition root itself (using Installers - described below).
Also see here and here for further discussion and justification for using a DI framework.
Hello World Example
using Zenject;
using UnityEngine;
using System.Collections;
public class TestInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<string>().FromInstance("Hello World!");
Container.Bind<Greeter>().AsSingle().NonLazy();
}
}
public class Greeter
{
public Greeter(string message)
{
Debug.Log(message);
}
}
You can run this example by doing the following:
- Create a new scene in Unity
- Right Click inside the Hierarchy tab and select
Zenject -> Scene Context
- Right Click in a folder within the Project Tab and Choose
Create -> Zenject -> MonoInstaller
. Name it TestInstaller.cs - Add your TestInstaller script to the scene (as its own GameObject or on the same GameObject as the SceneContext, it doesn't matter)
- Add a reference to your TestInstaller to the properties of the SceneContext by adding a new row in the inspector of the "Installers" property (press the + button) and then dragging TestInstaller to it
- Open up TestInstaller and paste the above code into it
- Validate your scene by either selecting Edit -> Zenject -> Validate Current Scene or hitting CTRL+ALT+V. (note that this step isn't necessary but good practice to get into)
- Run
- Note also, that you can use the shortcut
CTRL+SHIFT+R
to "validate then run". Validation is usually fast enough that this does not add a noticeable overhead to running your game, and when it does detect errors it is much faster to iterate on since you avoid the startup time. - Observe unity console for output
The SceneContext MonoBehaviour is the entry point of the application, where Zenject sets up all the various dependencies before kicking off your scene. To add content to your Zenject scene, you need to write what is referred to in Zenject as an 'Installer', which declares all the dependencies and their relationships with each other. All dependencies that are marked as "NonLazy" are automatically created after the installers are run, which is why the Greeter class that we added above gets created on startup. If this doesn't make sense to you yet, keep reading!
Injection
There are many different ways of declaring dependencies on the container, which are documented in the next section. There are also several ways of having these dependencies injected into your classes. These are:
1 - Constructor Injection
public class Foo
{
IBar _bar;
public Foo(IBar bar)
{
_bar = bar;
}
}
2 - Field Injection
public class Foo
{
[Inject]
IBar _bar;
}
Field injection occurs immediately after the constructor is called. All fields that are marked with the [Inject]
attribute are looked up in the container and given a value. Note that these fields can be private or public and injection will still occur.
3 - Property Injection
public class Foo
{
[Inject]
public IBar Bar
{
get;
private set;
}
}
Property injection works the same as field injection except is applied to C# properties. Just like fields, the setter can be private or public in this case.
4 - Method Injection
public class Foo
{
IBar _bar;
Qux _qux;
[Inject]
public Init(IBar bar, Qux qux)
{
_bar = bar;
_qux = qux;
}
}
Method Inject injection works very similarly to constructor injection.
Note the following:
- Inject methods are the recommended approach for MonoBehaviours, since MonoBehaviours cannot have constructors.
- There can be any number of inject methods. In this case, they are called in the order of Base class to Derived class. This can be useful to avoid the need to forward many dependencies from derived classes to the base class via constructor parameters, while also guaranteeing that the base class inject methods complete first, just like how constructors work.
- Inject methods are called after all other injection types. It is designed this way so that these methods can be used to execute initialization logic which might make use of injected fields or properties. Note also that you can leave the parameter list empty if you just want to do some initialization logic only.
- You can safely assume that the dependencies that you receive via inject methods will themselves already have been injected (the only exception to this is in the case where you have circular dependencies). This can be important if you use inject methods to perform some basic initialization, since in that case you may need the given dependencies to be initialized as well
- Note however that it is usually not a good idea to use inject methods for initialization logic. Often it is better to use IInitializable.Initialize or Start() methods instead, since this will allow the entire initial object graph to be created first.
Recommendations
Best practice is to prefer constructor/method injection compared to field/property injection.
- Constructor injection forces the dependency to only be resolved once, at class creation, which is usually what you want. In most cases you don't want to expose a public property for your initial dependencies because this suggests that it's open to changing.
- Constructor injection guarantees no circular dependencies between classes, which is generally a bad thing to do. Zenject does allow circular dependencies when using other injections types however such as method/field/property injection
- Constructor/Method injection is more portable for cases where you decide to re-use the code without a DI framework such as Zenject. You can do the same with public properties but it's more error prone (it's easier to forget to initialize one field and leave the object in an invalid state)
- Finally, Constructor/Method injection makes it clear what all the dependencies of a class are when another programmer is reading the code. They can simply look at the parameter list of the method. This is also good because it will be more obvious when a class has too many dependencies and should therefore be split up (since its constructor parameter list will start to seem long)
Binding
Every dependency injection framework is ultimately just a framework to bind types to instances.
In Zenject, dependency mapping is done by adding bindings to something called a container. The container should then 'know' how to create all the object instances in your application, by recursively resolving all dependencies for a given object.
When the container is asked to construct an instance of a given type, it uses C# reflection to find the list of constructor arguments, and all fields/properties that are marked with an [Inject]
attribute. It then attempts to resolve each of these required dependencies, which it uses to call the constructor and create the new instance.
Each Zenject application therefore must tell the container how to resolve each of these dependencies, which is done via Bind commands. For example, given the following class:
public class Foo
{
IBar _bar;
public Foo(IBar bar)
{
_bar = bar;
}
}
You can wire up the dependencies for this class with the following:
Container.Bind<Foo>().AsSingle();
Container.Bind<IBar>().To<Bar>().AsSingle();
This tells Zenject that every class that requires a dependency of type Foo should use the same instance, which it will automatically create when needed. And similarly, any class that requires the IBar interface (like Foo) will be given the same instance of type Bar.
The full format for the bind command is the following. Note that in most cases you will not use all of these methods and that they all have logical defaults when unspecified
Where:
ContractType = The type that you are creating a binding for.
- This value will correspond to the type of the field/parameter that is being injected.
ResultType = The type to bind to.
- Default: ContractType
- This type must either to equal to ContractType or derive from ContractType. If unspecified, it assumes ToSelf(), which means that the ResultType will be the same as the ContractType. This value will be used by whatever is given as the ConstructionMethod to retrieve an instance of this type
Identifier = The value to use to uniquely identify the binding. This can be ignored in most cases, but can be quite useful in cases where you need to distinguish between multiple bindings with the same contract type. See here for details.
ConstructionMethod = The method by which an instance of ResultType is created/retrieved. See this section for more details on the various construction methods.
- Default: FromNew()
- Examples: eg. FromGetter, FromMethod, FromResolve, FromComponentInNewPrefab, FromSubContainerResolve, FromInstance, etc.
Scope = This value determines how often (or if at all) the generated instance is re-used across multiple injections.
Default: AsTransient. Note however that not all bindings have a default, so an exception will be thrown if not supplied. The bindings that do not require the scope to be set explicitly are any binding with a construction method that is a search rather than creating a new object from scratch (eg. FromMethod, FromComponentX, FromResolve, etc.)
It can be one of the following:
- AsTransient - Will not re-use the instance at all. Every time ContractType is requested, the DiContainer will execute the given construction method again
- AsCached - Will re-use the same instance of ResultType every time ContractType is requested, which it will lazily generate upon first use
- AsSingle - Exactly the same as AsCached, except that it will sometimes throw exceptions if there already exists a binding for ResultType. It is simply a way to ensure that the given ResultType is unique within the container. Note however that it will only guarantee that there is only one instance across the given container, which means that using AsSingle with the same binding in a sub-container could generate a second instance.
In most cases, you will likely want to just use AsSingle, however AsTransient and AsCached have their uses too.
Arguments = A list of objects to use when constructing the new instance of type ResultType. This can be useful as an alternative to adding other bindings for the arguments in the form
Container.BindInstance(arg).WhenInjectedInto<ResultType>()
InstantiatedCallback = In some cases it is useful to be able customize an object after it is instantiated. In particular, if using a third party library, it might be necessary to change a few fields on one of its types. For these cases you can pass a method to OnInstantiated that can customize the newly created instance. For example:
Container.Bind<Foo>().AsSingle().OnInstantiated<Foo>(OnFooInstantiated); void OnFooInstantiated(InjectContext context, Foo foo) { foo.Qux = "asdf"; }
Or, equivalently:
Container.Bind<Foo>().AsSingle().OnInstantiated<Foo>((ctx, foo) => foo.Bar = "qux");
Note that you can also bind a custom factory using FromFactory that directly calls Container.InstantiateX before customizing it for the same effect, but OnInstantiated can be easier in some cases
Condition = The condition that must be true for this binding to be chosen. See here for more details.
(Copy|Move)Into(All|Direct)SubContainers = This value can be ignored for 99% of users. It can be used to automatically have the binding inherited by subcontainers. For example, if you have a class Foo and you want a unique instance of Foo to be automatically placed in the container and every subcontainer, then you could add the following binding:
Container.Bind<Foo>().AsSingle().CopyIntoAllSubContainers()
In other words, the result will be equivalent to copying and pasting the
Container.Bind<Foo>().AsSingle()
statement into the installer for every sub-container.Or, if you only wanted Foo in the subcontainers and not the current container:
Container.Bind<Foo>().AsSingle().MoveIntoAllSubContainers()
Or, if you only wanted Foo to be in the immediate child subcontainer, and not the subcontainers of these subcontainers:
Container.Bind<Foo>().AsSingle().MoveIntoDirectSubContainers()
NonLazy = Normally, the ResultType is only ever instantiated when the binding is first used (aka "lazily"). However, when NonLazy is used, ResultType will immediately be created on startup.
IfNotBound = When this is added to a binding and there is already a binding with the given contract type + identifier, then this binding will be skipped.
Construction Methods
FromNew - Create via the C# new operator. This is the default if no construction method is specified.
// These are both the same Container.Bind<Foo>(); Container.Bind<Foo>().FromNew();
FromInstance - Add a given instance to the container. Note that the given instance will not be injected in this case. If you also want your instance to be injected at startup, see QueueForInject
Container.Bind<Foo>().FromInstance(new Foo()); // You can also use this short hand which just takes ContractType from the parameter type Container.BindInstance(new Foo()); // This is also what you would typically use for primitive types Container.BindInstance(5.13f); Container.BindInstance("foo"); // Or, if you have many instances, you can use BindInstances Container.BindInstances(5.13f, "foo", new Foo());
FromMethod - Create via a custom method
Container.Bind<Foo>().FromMethod(SomeMethod); Foo SomeMethod(InjectContext context) { ... return new Foo(); }
FromMethodMultiple - Same as FromMethod except allows returning multiple instances at once (or zero).
Container.Bind<Foo>().FromMethodMultiple(GetFoos); IEnumerable<Foo> GetFoos(InjectContext context) { ... return new Foo[] { new Foo(), new Foo(), new Foo(), } }
FromFactory - Create instance using a custom factory class. This construction method is similar to
FromMethod
except can be cleaner in cases where the logic is more complicated or requires dependencies (since the factory itself can have dependencies injected)class FooFactory : IFactory<Foo> { public Foo Create() { // ... return new Foo(); } } Container.Bind<Foo>().FromFactory<FooFactory>()
FromIFactory - Create instance using a custom factory class. This is a more generic and more powerful alternative to FromFactory, because you can use any kind of construction method you want for your custom factory (unlike FromFactory which assumes
FromNew().AsCached()
)For example, you could use a factory that is a scriptable object like this:
class FooFactory : ScriptableObject, IFactory<Foo> { public Foo Create() { // ... return new Foo(); } } Container.Bind<Foo>().FromIFactory(x => x.To<FooFactory>().FromScriptableObjectResource("FooFactory")).AsSingle();
Or, you might want to have your custom factory be placed in a subcontainer like this:
public class FooFactory : IFactory<Foo> { public Foo Create() { return new Foo(); } } public override void InstallBindings() { Container.Bind<Foo>().FromIFactory(x => x.To<FooFactory>().FromSubContainerResolve().ByMethod(InstallFooFactory)).AsSingle(); } void InstallFooFactory(DiContainer subContainer) { subContainer.Bind<FooFactory>().AsSingle(); }
Also note that the following two lines are equivalent:
Container.Bind<Foo>().FromFactory<FooFactory>().AsSingle(); Container.Bind<Foo>().FromIFactory(x => x.To<FooFactory>().AsCached()).AsSingle();
FromComponentInNewPrefab - Instantiate the given prefab as a new game object, inject any MonoBehaviour's on it, and then search the result for type ResultType in a similar way that
GetComponentInChildren
worksContainer.Bind<Foo>().FromComponentInNewPrefab(somePrefab);
ResultType must either be an interface or derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
Note that if there are multiple matches for ResultType on the prefab it will only match the first one encountered just like how GetComponentInChildren works. So if you are binding a prefab and there isn't a specific MonoBehaviour/Component that you want to bind to, you can just choose Transform and it will match the root of the prefab.
FromComponentsInNewPrefab - Same as FromComponentInNewPrefab except will match multiple values or zero values. You might use it for example and then inject
List<ContractType>
somewhere. Can be thought of as similar toGetComponentsInChildren
FromComponentInNewPrefabResource - Instantiate the given prefab (found at the given resource path) as a new game object, inject any MonoBehaviour's on it, and then search the result for type ResultType in a similar way that
GetComponentInChildren
works (in that it will return the first matching value found)Container.Bind<Foo>().FromComponentInNewPrefabResource("Some/Path/Foo");
ResultType must either be an interface or derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
FromComponentsInNewPrefabResource - Same as FromComponentInNewPrefab except will match multiple values or zero values. You might use it for example and then inject
List<ContractType>
somewhere. Can be thought of as similar toGetComponentsInChildren
FromNewComponentOnNewGameObject - Create a new empty game object and then instantiate a new component of the given type on it.
Container.Bind<Foo>().FromNewComponentOnNewGameObject();
ResultType must derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
FromNewComponentOnNewPrefab - Instantiate the given prefab as a new game object and also instantiate a new instance of the given component on the root of the new game object. NOTE: It is not necessary that the prefab contains a copy of the given component.
Container.Bind<Foo>().FromNewComponentOnNewPrefab(somePrefab);
ResultType must derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
FromNewComponentOnNewPrefabResource - Instantiate the given prefab (found at the given resource path) and also instantiate a new instance of the given component on the root of the new game object. NOTE: It is not necessary that the prefab contains a copy of the given component.
Container.Bind<Foo>().FromNewComponentOnNewPrefabResource("Some/Path/Foo");
ResultType must derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
FromNewComponentOn - Instantiate a new component of the given type on the given game object
Container.Bind<Foo>().FromNewComponentOn(someGameObject);
ResultType must derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
FromNewComponentSibling - Instantiate a new component of the given on the current transform. The current transform here is taken from the object being injected, which must therefore be a MonoBehaviour derived type.
Container.Bind<Foo>().FromNewComponentSibling();
Note that if the given component type is already attached to the current transform that this will just return that instead of creating a new component. As a result, this bind statement functions similar to Unity's [RequireComponent] attribute.
ResultType must derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
Also note that if a non-MonoBehaviour requests the given type, an exception will be thrown, since there is no current transform in that case.
FromComponentInHierarchy - Look up the component within the scene hierarchy associated with the current context, as well as the hierarchy associated with any parent contexts. Works similar to
GetComponentInChildren
in that it will return the first matching value foundContainer.Bind<Foo>().FromComponentInHierarchy().AsSingle();
ResultType must either be an interface or derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
In the most common case where the context is a SceneContext, this will search the entire scene hierarchy (except any sub-contexts such as GameObjectContext). In other words, when the current context is a scene context, it will behave similar to
GameObject.FindObjectsOfType
. Note that since this could be a big search, it should be used with caution, just likeGameObject.FindObjectsOfType
should be used with caution.In the case where the context is GameObjectContext, it will only search within and underneath the game object root (and any parent contexts).
In the case where the context is ProjectContext, it will only search within the project context prefab
FromComponentsInHierarchy - Same as FromComponentInHierarchy except will match multiple values or zero values. You might use it for example and then inject
List<ContractType>
somewhere. Can be thought of as similar toGetComponentsInChildren
FromComponentSibling - Look up the given component type by searching over the components that are attached to the current transform. The current transform here is taken from the object being injected, which must therefore be a MonoBehaviour derived type.
Container.Bind<Foo>().FromComponentSibling();
ResultType must either be an interface or derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
Note that if a non-MonoBehaviour requests the given type, an exception will be thrown, since there is no current transform in that case.
FromComponentsSibling - Same as FromComponentSibling except will match multiple values or zero values.
FromComponentInParents - Look up the component by searching the current transform or any parent for the given component type. The current transform here is taken from the object being injected, which must therefore be a MonoBehaviour derived type.
Container.Bind<Foo>().FromComponentInParents();
ResultType must either be an interface or derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
Note that if a non-MonoBehaviour requests the given type, an exception will be thrown, since there is no current transform in that case.
FromComponentsInParents - Same as FromComponentInParents except will match multiple values or zero values. You might use it for example and then inject
List<ContractType>
somewhereFromComponentInChildren - Look up the component by searching the current transform or any child transform for the given component type. The current transform here is taken from the object being injected, which must therefore be a MonoBehaviour derived type. Similar to
GetComponentInChildren
in that it will return the first matching value foundContainer.Bind<Foo>().FromComponentInChildren();
ResultType must either be an interface or derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
Note that if a non-MonoBehaviour requests the given type, an exception will be thrown, since there is no current transform in that case.
FromComponentsInChildren - Same as FromComponentInChildren except will match multiple values or zero values. You might use it for example and then inject
List<ContractType>
somewhere. Can be thought of as similar toGetComponentsInChildren
FromNewComponentOnRoot - Instantiate the given component on the root of the current context. This is most often used with GameObjectContext.
Container.Bind<Foo>().FromNewComponentOnRoot();
ResultType must derive from UnityEngine.MonoBehaviour / UnityEngine.Component in this case
FromResource - Create by calling the Unity3d function
Resources.Load
for ResultType. This can be used to load any type thatResources.Load
can load, such as textures, sounds, prefabs, etc.Container.Bind<Texture>().WithId("Glass").FromResource("Some/Path/Glass");
FromResources - Same as FromResource except will match multiple values or zero values. You might use it for example and then inject
List<ContractType>
somewhereFromScriptableObjectResource - Bind directly to the scriptable object instance at the given resource path. NOTE: Changes to this value while inside unity editor will be saved persistently. If this is undesirable, use FromNewScriptableObjectResource.
public class Foo : ScriptableObject { } Container.Bind<Foo>().FromScriptableObjectResource("Some/Path/Foo");
FromNewScriptableObjectResource - Same as FromScriptableObjectResource except it will instantiate a new copy of the given scriptable object resource. This can be useful if you want to have multiple distinct instances of the given scriptable object resource, or if you want to ensure that the saved values for the scriptable object are not affected by changing at runtime.
FromResolve - Get instance by doing another lookup on the container (in other words, calling
DiContainer.Resolve<ResultType>()
). Note that for this to work, ResultType must be bound in a separate bind statement. This construction method can be especially useful when you want to bind an interface to another interface, as shown in the below examplepublic interface IFoo { } public interface IBar : IFoo { } public class Foo : IBar { } Container.Bind<IFoo>().To<IBar>().FromResolve(); Container.Bind<IBar>().To<Foo>();
FromResolveAll - Same as FromResolve except will match multiple values or zero values.
FromResolveGetter<ObjectType> - Get instance from the property of another dependency which is obtained by doing another lookup on the container (in other words, calling
DiContainer.Resolve<ObjectType>()
and then accessing a value on the returned instance of type ResultType). Note that for this to work, ObjectType must be bound in a separate bind statement.public class Bar { } public class Foo { public Bar GetBar() { return new Bar(); } } Container.Bind<Foo>(); Container.Bind<Bar>().FromResolveGetter<Foo>(x => x.GetBar());
FromResolveAllGetter<ObjectType> - Same as FromResolveGetter except will match multiple values or zero values.
FromSubContainerResolve - Get ResultType by doing a lookup on a subcontainer. Note that for this to work, the sub-container must have a binding for ResultType. This approach can be very powerful, because it allows you to group related dependencies together inside a mini-container, and then expose only certain classes (aka "Facades") to operate on this group of dependencies at a higher level. For more details on using sub-containers, see this section. There are several ways to define the subcontainer:
ByNewPrefabMethod - Initialize subcontainer by instantiating a new prefab. Note that unlike
ByNewContextPrefab
, this bind method does not require that there be a GameObjectContext attached to the prefab. In this case the GameObjectContext is added dynamically and then run with the given installer method.Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabMethod(MyPrefab, InstallFoo); void InstallFoo(DiContainer subContainer) { subContainer.Bind<Foo>(); }
ByNewPrefabInstaller - Initialize subcontainer by instantiating a new prefab. Same as ByNewPrefabMethod, except it initializes the dynamically created GameObjectContext with the given installer rather than a method.
Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabInstaller<FooInstaller>(MyPrefab); class FooInstaller : Installer { public override void InstallBindings() { Container.Bind<Foo>(); } }
ByNewPrefabResourceMethod - Initialize subcontainer instantiating a new prefab obtained via
Resources.Load
. Note that unlikeByNewPrefabResource
, this bind method does not require that there be a GameObjectContext attached to the prefab. In this case the GameObjectContext is added dynamically and then run with the given installer method.Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabResourceMethod("Path/To/MyPrefab", InstallFoo); void InstallFoo(DiContainer subContainer) { subContainer.Bind<Foo>(); }
ByNewPrefabResourceInstaller - Initialize subcontainer instantiating a new prefab obtained via
Resources.Load
. Same as ByNewPrefabResourceMethod, except it initializes the dynamically created GameObjectContext with the given installer rather than a method.Container.Bind<Foo>().FromSubContainerResolve().ByNewPrefabResourceInstaller<FooInstaller>("Path/To/MyPrefab"); class FooInstaller : MonoInstaller { public override void InstallBindings() { Container.Bind<Foo>(); } }
ByNewGameObjectInstaller - Initialize subcontainer by instantiating a empty game object, attaching GameObjectContext, and then installing using the given installer. This approach is very similar to ByInstaller except has the following advantages:
- Any ITickable, IInitializable, IDisposable, etc. bindings will be called properly
- Any new game objects that are instantiated inside the subcontainer will be parented underneath the game object context object
- You can destroy the subcontainer by just destroying the game object context game object
ByNewGameObjectMethod - Same as ByNewGameObjectInstaller except the subcontainer is initialized based on the given method rather than an installer type.
ByMethod - Initialize the subcontainer by using a method.
Container.Bind<Foo>().FromSubContainerResolve().ByMethod(InstallFooFacade); void InstallFooFacade(DiContainer subContainer) { subContainer.Bind<Foo>(); }
Note that when using ByMethod, if you want to use zenject interfaces such as ITickable, IInitializable, IDisposable inside your subcontainer then you have to also use the WithKernel bind method like this:
Container.Bind<Foo>().FromSubContainerResolve().ByMethod(InstallFooFacade).WithKernel(); void InstallFooFacade(DiContainer subContainer) { subContainer.Bind<Foo>(); subContainer.Bind<ITickable>().To<Bar>(); }
ByInstaller - Initialize the subcontainer by using a class derived from
Installer
. This can be a cleaner and less error-prone alternative thanByMethod
, especially if you need to inject data into the installer itself. Less error prone because when using ByMethod it is common to accidentally use Container instead of subContainer in your method.Container.Bind<Foo>().FromSubContainerResolve().ByInstaller<FooFacadeInstaller>(); class FooFacadeInstaller : Installer { public override void InstallBindings() { Container.Bind<Foo>(); } }
Note that when using ByInstaller, if you want to use zenject interfaces such as ITickable, IInitializable, IDisposable inside your subcontainer then you have to also use the WithKernel bind method like this:
Container.Bind<Foo>().FromSubContainerResolve().ByInstaller<FooFacadeInstaller>().WithKernel();
ByNewContextPrefab - Initialize subcontainer by instantiating a new prefab. Note that the prefab must contain a
GameObjectContext
component attached to the root game object. For details onGameObjectContext
see this section.Container.Bind<Foo>().FromSubContainerResolve().ByNewContextPrefab(MyPrefab); // Assuming here that this installer is added to the GameObjectContext at the root // of the prefab. You could also use a ZenjectBinding in the case where Foo is a MonoBehaviour class FooFacadeInstaller : MonoInstaller { public override void InstallBindings() { Container.Bind<Foo>(); } }
ByNewContextPrefabResource - Initialize subcontainer instantiating a new prefab obtained via
Resources.Load
. Note that the prefab must contain aGameObjectContext
component attached to the root game object.Container.Bind<Foo>().FromSubContainerResolve().ByNewContextPrefabResource("Path/To/MyPrefab");
ByInstance - Initialize the subcontainer by directly using a given instance of DiContainer that you provide yourself. This is only useful in some rare edge cases.
FromSubContainerResolveAll - Same as FromSubContainerResolve except will match multiple values or zero values.
Installers
Often, there is some collections of related bindings for each sub-system and so it makes sense to group those bindings into a re-usable object. In Zenject this re-usable object is called an 'installer'. You can define a new installer as follows:
public class FooInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<Bar>().AsSingle();
Container.BindInterfacesTo<Foo>().AsSingle();
// etc...
}
}
You add bindings by overriding the InstallBindings method, which is called by whatever Context
the installer has been added to (usually this is SceneContext
). MonoInstaller is a MonoBehaviour so you can add FooInstaller by attaching it to a GameObject. Since it is a GameObject you can also add public members to it to configure your installer from the Unity inspector. This allows you to add references within the scene, references to assets, or simply tuning data (see here for more information on tuning data).
Note that in order for your installer to be triggered it must be attached to the Installers property of the SceneContext
object. Installers are installed in the order given to SceneContext
(with scriptable object installers first, then mono installers, then prefab installers) however this order should not usually matter (since nothing should be instantiated during the install process).
In many cases you want to have your installer derive from MonoInstaller, so that you can have inspector settings. There is also another base class called simply Installer
which you can use in cases where you do not need it to be a MonoBehaviour.
You can also call an installer from another installer. For example:
public class BarInstaller : Installer<BarInstaller>
{
public override void InstallBindings()
{
...
}
}
public class FooInstaller : MonoInstaller
{
public override void InstallBindings()
{
BarInstaller.Install(Container);
}
}
Note that in this case BarInstaller is of type Installer<>
(note the generic arguments) and not MonoInstaller, which is why we can simply call BarInstaller.Install(Container)
and don't require that BarInstaller be added to our scene already. Any calls to BarInstaller.Install will immediately instantiate a temporary instance of BarInstaller and then call InstallBindings on it. This will repeat for any installers that this installer installs. Note also that when using the Installer<>
base class, we always must pass in ourself as the generic argument to Installer<>
. This is necessary so that the Installer<>
base class can define the static method BarInstaller.Install
. It is also designed this way to support runtime parameters (described below).
One of the main reasons we use installers as opposed to just having all our bindings declared all at once for each scene, is to make them re-usable. This is not a problem for installers of type Installer<>
because you can simply call FooInstaller.Install
as described above for every scene you wish to use it in, but then how would we re-use a MonoInstaller in multiple scenes?
There are three ways to do this.
Prefab instances within the scene. After attaching your MonoInstaller to a gameobject in your scene, you can then create a prefab out of it. This is nice because it allows you to share any configuration that you've done in the inspector on the MonoInstaller across scenes (and also have per-scene overrides if you want). After adding it in your scene you can then drag and drop it on to the Installers property of a
Context
Prefabs. You can also directly drag your installer prefab from the Project tab into the InstallerPrefabs property of SceneContext. Note that in this case you cannot have per-scene overrides like you can when having the prefab instantiated in your scene, but can be nice to avoid clutter in the scene.
Prefabs within Resources folder. You can also place your installer prefabs underneath a Resoures folder and install them directly from code by using the Resources path. For details on usage see here.
Another option in addition to MonoInstaller
and Installer<>
is to use ScriptableObjectInstaller
which has some unique advantages (especially for settings) - for details see here.
When calling installers from other installers it is common to want to pass parameters into it. See here for details on how that is done.
ITickable
In some cases it is preferable to avoid the extra weight of MonoBehaviours in favour of just normal C# classes. Zenject allows you to do this much more easily by providing interfaces that mirror functionality that you would normally need to use a MonoBehaviour for.
For example, if you have code that needs to run per frame, then you can implement the ITickable
interface:
public class Ship : ITickable
{
public void Tick()
{
// Perform per frame tasks
}
}
Then, to hook it up in an installer:
Container.Bind<ITickable>().To<Ship>().AsSingle();
Or if you don't want to have to always remember which interfaces your class implements, you can use the shortcut described here
Note that the order that the Tick() is called in for all ITickables is also configurable, as outlined here.
Also note that there are interfaces ILateTickable
and IFixedTickable
which mirror Unity's LateUpdate and FixedUpdated methods
IInitializable
If you have some initialization that needs to occur on a given object, you could include this code in the constructor. However, this means that the initialization logic would occur in the middle of the object graph being constructed, so it may not be ideal.
A better alternative is to implement IInitializable
, and then perform initialization logic in an Initialize()
method.
Then, to hook it up in an installer:
Container.Bind<IInitializable>().To<Foo>().AsSingle();
Or if you don't want to have to always remember which interfaces your class implements, you can use the shortcut described here
The Foo.Initialize
method would then be called after the entire object graph is constructed and all constructors have been called.
Note that the constructors for the initial object graph are called during Unity's Awake event, and that the IInitializable.Initialize
methods are called immediately on Unity's Start event. Using IInitializable
as opposed to a constructor is therefore more in line with Unity's own recommendations, which suggest that the Awake phase be used to set up object references, and the Start phase be used for more involved initialization logic.
This can also be better than using constructors or [Inject]
methods because the initialization order is customizable in a similar way to ITickable
, as explained here.
public class Ship : IInitializable
{
public void Initialize()
{
// Initialize your object here
}
}
IInitializable
works well for start-up initialization, but what about for objects that are created dynamically via factories? (see this section for what I'm referring to here). For these cases you will most likely want to eitehr use an [Inject]
method or an explicit Initialize m