VContainer Documentation

repository·master·Indexed 25 days ago

https://github.com/hadashia/vcontainer

A high-performance, low-allocation Dependency Injection (DI) library designed specifically for the Unity Game Engine. VContainer provides a lightweight alternative to Zenject, focusing on minimal GC allocation and performance. It features LifetimeScope for dependency configuration, support for constructor injection with keys, dynamic child scope creation, and integration with UniTask via IAsyncStartable. The library allows developers to separate domain logic from presentation by moving control flow into pure C# classes.

Tokens
20.7K
Snippets
69
Records
114
Agent score
83%

What's inside VContainer

  1. Understand Dependency Injection (DI) terminology

    master

    When using VContainer, it is helpful to understand the following core concepts:

    • DI Container: A central registry that holds all dependent references and performs auto-wiring.
    • Composition Root: The specific location in your application where you configure and resolve dependencies.
    • Auto-wiring: The process of automatically managing and injecting services into classes with minimal manual configuration.
    • IoC (Inversion of Control): A design principle where the control flow responsibility is shifted to an entry point, allowing for better separation of concerns.
  2. Understand VContainer Lifetimes

    master

    VContainer provides three primary lifetime modes for registered objects:

    • Singleton: A single instance is shared across all containers. Note that the same type cannot be registered multiple times within the same container.
    • Transient: A new instance is created every time the object is resolved.
    • Scoped: An instance is tied to a specific LifetimeScope.
      • If the LifetimeScope is a singleton, it behaves similarly to a Singleton.
      • If you create child LifetimeScope instances, each child receives its own unique instance.
      • When a LifetimeScope is destroyed, all registered objects implementing IDisposable have their Dispose() method called.
  3. Inject dependencies into MonoBehaviours

    master

    Because MonoBehaviours do not support constructors, you must use method injection (marking methods with the [Inject] attribute) to provide dependencies. Note that simply adding the [Inject] attribute is not enough; you must trigger the injection using one of the following three methods:

    1. LifetimeScope Inspector: Specify specific GameObjects in the LifetimeScope inspector. VContainer will automatically inject all MonoBehaviours on those GameObjects and their children when the LifetimeScope is initialized.
    2. RegisterComponent Methods: Use RegisterComponent* methods to register a MonoBehaviour instance to the container. This allows the instance to both receive injections and be injected into other classes.
    3. IObjectResolver.Instantiate: For dynamically-generated MonoBehaviours (like those from prefabs), use IObjectResolver.Instantiate instead of UnityEngine.Object.Instantiate. This ensures the new instance is properly injected upon creation.
  4. Enable parallel container builds with VCONTAINER_PARALLEL_CONTAINER_BUILD

    master

    To optimize container build performance when registering a large number of objects, you can enable parallel container builds by setting the VCONTAINER_PARALLEL_CONTAINER_BUILD compilation flag.

    Warning: Enabling this flag may result in slower build times if you have only a few objects to register. Use this primarily for large-scale registrations.

  5. Install VContainer via UPM (Git URL)

    master

    To install VContainer using Unity's Package Manager, add the following line to the dependencies section of your project's Packages/manifest.json file.

    "jp.hadashikick.vcontainer": "https://github.com/hadashiA/VContainer.git?path=VContainer/Assets/VContainer#1.19.0"
  6. Integrate VContainer into a Unity scene

    master

    To integrate VContainer, follow these steps:

    1. Create a LifetimeScope: Create a class that inherits from LifetimeScope. This acts as your composition root where you register dependencies.
    2. Register Dependencies: Override the Configure method in your LifetimeScope subclass and use the IContainerBuilder to register your classes.
    3. Attach to GameObject: Create an empty GameObject in your Unity scene and attach your LifetimeScope component to it. VContainer will automatically build the container and dispatch to the PlayerLoopSystem when the scene plays.
    public class GameLifetimeScope : LifetimeScope
    {
        protected override void Configure(IContainerBuilder builder)
        {
            builder.Register<HelloWorldService>(Lifetime.Singleton);
        }
    }
  7. Understand Lifetime behavior in Parent/Child relationships

    master

    When using LifetimeScope hierarchies, the container follows these resolution rules:

    • Resolution Lookup: If a requested object is not found in the current LifetimeScope, the container will search up the hierarchy to the parent LifetimeScope.
    • Lifetime.Singleton: Returns the same instance within its scope. If both parent and child have a registration for the same type, the container returns the instance from the closest scope.
    • Lifetime.Transient: Always creates a new instance for every resolution request. If a child has its own registration for a type, it will use its own instance instead of the parent's.
    • Lifetime.Scoped: Instances are unique to each LifetimeScope.
      • Within the same child scope, the same instance is returned.
      • If a child has its own registration for a type, it creates its own instance.
      • When a LifetimeScope is destroyed, all registered objects implementing IDisposable are disposed.
  8. Register Instances for the Next Loaded Scene

    master

    You can use LifetimeScope.Enqueue to register specific instances that will be available to the next scene that is loaded. This is useful for passing data or services across scene boundaries during a loading process.

    // LifetimeScopes generated during this block will be additionally Registered.
    using (LifetimeScope.Enqueue(builder =>
    {
        // Register for the next scene not yet loaded
        builder.RegisterInstance(extraInstance);
    }))
    {
        // Loading the scene..
    }
  9. Parent child relationships in Additive Scenes

    master

    When loading scenes additively, you can use LifetimeScope.EnqueueParent to ensure that any LifetimeScope generated within that block is parented to a specific scope.

    class SceneLoader
    {
        readonly LifetimeScope currentScope;
    
        public SceneLoader(LifetimeScope currentScope)
        {
            this.currentScope = currentScope;
        }
    
        IEnumerator LoadSceneAsync()
        {
            // LifetimeScope generated in this block will be parented by `this.currentScope`
            using (LifetimeScope.EnqueueParent(currentScope))
            {
                var loading = SceneManager.LoadSceneAsync("...", LoadSceneMode.Additive);
                while (!loading.isDone)
                {
                    yield return null;
                }
            }
        }
    }