Asynchronous DI Container for Unity
Install via Unity Package Manager Please install the packages in the following order:
https://github.com/mewlist/MewCore.git
https://github.com/mewlist/Doinject.git
Doinject is an asynchronous DI (Dependency Injection) framework for Unity.
The concept of asynchronous DI containers is the starting point. Unity 2022 LTS / Unity 6 are supported.
Typical DI containers perform a synchronous process when generating registered types. However, it cannot support the asynchronous instantiation function used in Unity or instance creation methods that require some kind of asynchronous processing.
By introducing Doinject, you can use a DI container that supports instance creation and release through asynchronous processing. This allows instance creation with asynchronous loading through Addressables Asset Systems, or instance creation based on information loaded from asynchronous IO. It becomes possible to perform more flexible instance management with simple descriptions. By creating a custom factory, you can create instances that involve any asynchronous processing.
Designed to define context spaces in a way that does not conflict with Unity's lifecycle. When a scene is closed, the context associated with that scene is closed, the instances created in that context space disappear, and destroying a GameObject with context similarly closes the context. Context spaces are automatically structured by the framework, and parent-child relationships are formed when multiple contexts are loaded.
Instances from the Addressable Asset System can also be handled, and the release of load handles can be automated. Resource management in Addressables requires careful implementation, such as creating your own resource management system. However, using Doinject automates the loading and release of Addressables.
You can achieve replacements for the factory pattern, (context-closed) singleton pattern, and service locator pattern with simple descriptions. Additionally, by creating custom factories or custom resolvers, you can handle more complex instance creation scenarios.
Code | Resolver behavior | Type |
---|---|---|
container.Bind<SomeClass>(); |
new SomeClass() |
cached |
container.Bind<SomeClass>().AsSingleton(); |
new SomeClass() |
singleton |
container.Bind<SomeClass>().AsTransient(); |
new SomeClass() |
transient |
container.Bind<SomeClass>().Args(123,"ABC"); |
new SomeClass(123, "abc") |
cached |
container.Bind<ISomeInterface>().To<SomeClass>(); |
new SomeClass() as ISomeInterface |
cached |
container.Bind<ISomeInterface, SomeClass>(); |
new SomeClass() as ISomeInterface |
cached |
container.Bind<SomeClass>() .FromInstance(instance); |
instance |
instance |
container.BindInstance(instance); |
instance |
instance |
Code | Resolver behavior |
---|---|
container.Bind<SomeComponent>(); |
new GameObject().AddComponent<SomeComponent>() |
container .Bind<SomeComponent>() .Under(transform); |
var instance = new GameObject().AddComponent<SomeComponent>(); instance.transform.SetParent(transform); |
container .Bind<SomeComponent>() .On(gameObject); |
gameObject.AddComponent<SomeComponent>() |
container .BindPrefab<SomeComponent>(somePrefab); |
Instantiate(somePrefab).GetComponent<SomeComponent>() |
Code | Resolver behavior |
---|---|
container .BindAssetReference<SomeAddressalbesObject>(assetReference); |
var handle = Addressables .LoadAssetAsync<GameObject>(assetReference) await handle.Task |
container .BindPrefabAssetReference<SomeComponent>(prefabAssetReference); |
var handle = Addressables .LoadAssetAsync<GameObject>(prefabAssetReference) var prefab = await handle.Task Instantiate(prefab).GetComponent<SomeComponent>() |
container .BindAssetRuntimeKey<SomeAddressalbesObject>("guid or path"); |
var handle = Addressables .LoadAssetAsync<GameObject>("guid or path") await handle.Task |
container .BindPrefabAssetRuntimeKey<SomeComponent>("guid or path"); |
var handle = Addressables .LoadAssetAsync<GameObject>("guid or path") var prefab = await handle.Task Instantiate(prefab).GetComponent<SomeComponent>() |
Code | Resolver behavior |
---|---|
container .Bind<SomeClass>() .AsFactory(); |
var resolver = new TypeResolver<SomeClass>() new Factory<SomeClass>(resolver) as IFactory<SomeClass> |
container .Bind<SomeComponent>() .AsFactory(); |
var resolver = new MonoBehaviourResolver<SomeComponent>() new Factory<SomeComponent>(resolver)) as IFactory<SomeComponent> |
container .Bind<SomeClass>() .AsCustomFactory<MyFactory>(); |
new CustomFactoryResolver<MyFactory>() as IFactory<SomeClass> |
Can also be combined with Addressables. You can also create a factory that instantiates the asynchronously loaded prefab and calls GetComponent() on it.
container
.BindAssetReference<SomeComponentOnAddressalbesPrefab>(assetReference)
.AsFactory<SomeComponentOnAddressalbesPrefab>();
[Inject]
void Construct(IFactory<SomeComponentOnAddressalbesPrefab> factory)
{
var instance = await factory.CreateAsync();
}
public class SomeInstaller : BindingInstallerScriptableObject
{
public override void Install(DIContainer container, IContextArg contextArg)
{
container.Bind<SomeClass>();
}
}
class ExampleClass
{
// Constructor Injection
public ExampleClass(SomeClass someClass)
{ ... }
}
class ExampleClass
{
// Method Injection
[Inject]
public Construct(SomeClass someClass)
{ ... }
}
// Inherits IInjectableComponent
class ExampleComponent : MonoBehaviour, IInjectableComponent
{
// Method Injection
[Inject]
public void Construct(SomeClass someClass)
{ ... }
}