Skip to content

Betauer.DI: Dependency Injection for Godot 4

Alberto Vilches edited this page Apr 13, 2023 · 2 revisions

Introduction

This document provides an overview of the Dependency Injection (DI) system developed for the Godot game engine in C#. The DI system can be used standalone or with attributes, which allows you to easily manage and control the lifecycle of your game objects and services.

To use the Dependency Injection system, you need to have a basic understanding of the Godot game engine, C# programming, and dependency injection concepts.

There are two ways to interact with the container:

  • Manually: adding and resolving (getting) services from the container.
  • With attributes: using attributes to mark your classes as services and letting to the the built-in scanner to manage (adding them to the container).

Lifetime

There are two types of services based on its Lifetime.

  • Singleton: there is only one instance of each singleton in the container. Every time you request for a singleton, the container will return the same instance. The container will create (using default constructor or your own factory to build it) all the singleton in the build process, unless the singletons are marked as lazy. More info about this later.
  • Transient: every time you request for a transient service, the container will create a new instance.

Dependency injection

Every time a service is requested, the container will find all properties or methods tagged with the [Inject] attribute and try to resolve and set them. Transients instances can have inside singletons, but a singleton can not have transients (reason here)

Singleton The [Singleton] attribute indicates that a class should be treated as a Singleton. This means that only one instance of the class will be created and shared across the entire application. By default, Singleton instances are eagerly initialized during the bootstrap process. However, you can modify this behavior using the Lazy field:

csharp Copy code [Singleton(Lazy = true)] public class MyLazySingleton { ... } Transient The [Transient] attribute indicates that a class should be treated as a Transient. This means that a new instance of the class will be created each time it's requested.

csharp Copy code [Transient] public class MyTransientService { ... } Inject The [Inject] attribute is used to mark a field or property where a dependency should be injected. This can be applied to any class to automatically inject the required services.

csharp Copy code public class MyClass { [Inject] public MySingletonService _singletonService; } Scanning and Initialization Process The scanning process starts in an Autoload node, which is a special node loaded during the bootstrap process. When a node is added to the scene tree, the DI system performs the injection process. All Singleton instances are eagerly instantiated after the first scan in the bootstrap.

Lazy Initialization By default, Singleton instances are eagerly initialized during the bootstrap process. If you want to delay the initialization of a Singleton until it's actually used, you can set the Lazy field to true:

csharp Copy code [Singleton(Lazy = true)] public class MyLazySingleton { ... } When the Lazy field is set to true, the Singleton will not be instantiated until it's first injected into a node.

Using IFactory for Lazy Singletons To further delay the instantiation of a Lazy Singleton until it's explicitly used, you can use the [Inject] attribute with an IFactory:

csharp Copy code public class MyClass { [Inject] public IFactory _factory; } By using an IFactory, the DI system will inject a factory for the service instead of directly injecting the instance. This way, the Lazy Singleton will not be created until the user calls the IFactory.Get() method.

Examples Using a Singleton:

csharp Copy code [Singleton] public class MySingletonService { ... }

public class MyClass { [Inject] public MySingletonService _singletonService; } Using a Transient:

csharp Copy code [Transient] public class MyTransientService { ... }

public class

Alberto Vilches continue MyClass { [Inject] public MyTransientService _transientService; }

kotlin Copy code

Using a Lazy Singleton:

[Singleton(Lazy = true)]
public class MyLazySingleton
{
    ...
}

public class MyClass
{
    [Inject]
    public MyLazySingleton _lazySingleton;
}
Using an IFactory for a Lazy Singleton:

csharp
Copy code
[Singleton(Lazy = true)]
public class MyLazySingleton
{
    ...
}

public class MyClass
{
    [Inject]
    public IFactory<MyLazySingleton> _factory;
}
Conclusion
The Dependency Injection system for Godot in C# provides a flexible way to manage the lifecycle of your game objects and services. By using attributes like [Singleton], [Transient], [Inject], and [Configuration], you can specify the desired behavior for your classes and their dependencies, as well as control how instances are created. The support for Lazy Singletons, IFactory<T>, and Configuration classes allows you to optimize the initialization process and manage the creation of your services more efficiently.
Clone this wiki locally