You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For brevity sake, this gist assumes that you don't need convincing regarding DI in Unity.
Still, even without any focus on convincing,
it can be beneticial to read a refresher about the topic of purpose and usefullness of DI in Unity.
When DI is not used, Singletons become unavoidable. Singletons are not compatible with unit-tests and reuse,
but it's not losing access to them that is important here.
When something is not compatible with them, it's a symptom of a bigger illness incoming:
rigid architecture that will react poorly to changes (e.g. in game design) and will limit the number of available decisions (e.g. in game design).
When DI is used but not IoC, it's still hard to avoid using having at least one Singleton.
One way to avoid it is to use Service Locator but it's an infamous anti-pattern.
(While noticable number people still defend Singleton, almost nobody defends Service Locator.)
In traditional C# code it's easy to avoid having a DI container
by keeping all dependencies in constructors and initializing everything in Main.
In Unity, there are MonoBehaviours that can't be initialized in this fashion. Also, in Unity, there's no Main.
Thus, in Unity, implicit DI is not possible, i.e. one has to explicitly use 3rd-party container or write one's own one.
Because of point 2 above, I'll focus on IoC container and mostly ignore pure DI.
Be careful, multiple famous IoC containers are deprecated.
At a first glance, it may seem fine to forgive lack of recent commits.
After all, if it's mature, then all the basics are done and polished and bugless.
In other words, if it the project would be active, then all the new commits would be just exlusively about advanced features.
Right? No. The world of game development doesn't work like that.
It must innovate rapidly and fretting about backward compatibility is not compatible with this reality.
And lack of fretting about backward compatibility leads to fast library rot.
I.e. the next or next after next major version of game engine will be incompatible with it.
Notable deprecated IoC containers: Adic, Zenject¹ (use Extenject instead).
¹ Yes, technically, Zenject is maintained. But after the Zenject controversy there's no point in favoring it over more active Extenject.
Current libraries
IoC container is such a pattern that its implementations become nearly indistinguishable from each other once they mature.
Still, you have to choose one and choose wisely.
Even a small quirk may become very irritating after years of dealing with it
if it's particularly noticable in your project.
StrangeIoC:
Assists in avoiding MonoBehaviours.
In other words, its design assumes that you want to minimize MonoBehaviour usage.
(Thus, if your project wants MonoBehaviours, prefer some other IoC container.)
Beginner-friendly because of rich documentation and popularity (big community).
That is, if you're already skilled enough to be capable of either minimizing usage of MonoBehaviour or of extending your IoC container with richer support of MonoBehaviour.
Extenject: Beginner-friendly because of rich documentation and popularity (big community).
Instead of implementing DI, it uses the new built-in IoC container that comes out of the box with .NET now.
If I interpreted its readme correctly, regarding Unity support, only registering MonoBehaviours as dependencies is implemented. I.e. there's no instantiation with injection or scene handling out of the box.
VContainer: Intended to be more performance-optimization-friendly than Extenject.
Reflex: Intended to be even more performant than VContainer.
UniDi: The next thing from the author of Extenject.
Impossible Odds:
A whole toolkit instead of just a single IoC container. (I don't need to tell you why toolkits are both a blessing and a curse.)
UIComponents — Framework for Unity's UIToolkit. It has a simple dependency injection that is intended only for UI purposes. This way you can use DI in UI only and continue using classic (non-DI) architecture approach in the rest of codebase.