1
\$\begingroup\$

I decided to make my own light DI container.

I know about Zenject, etc. I wanted a very light analog.

I'm concerned about a few things:

1) is it correct to use struct instead of class for reference in this context?

2) every time you access _player.Reference, there is a check for null (in the UnityEngine.After the destruction of the Object does not equal null, so use SafeIsUnityNull)

3) Double point. Can I overload the operator so that instead of _player.Reference.AAAAAAAAAAAAA();, it works _player.AAAAAAAAAAAAA(); ??

Container itself:

    public static class DIContainer
    {

        private static readonly Dictionary<System.Type, Object> _references = new Dictionary<System.Type, Object>();

        public static void Bind<T>(T value) where T : Object
        {

            _references[typeof(T)] = value;

        }

        public static T Resolve<T>() where T : Object
        {

            if (_references.TryGetValue(typeof(T), out Object value))
                return (T)value;

            throw new System.InvalidCastException($"DIContainer: invalid resolve object {typeof(T)}");

        }

    }

Field reference:

    public struct DIReference<T> where T : Object
    {

        private T _reference;
        public T Reference => !_reference.SafeIsUnityNull() ? _reference : _reference = DIContainer.Resolve<T>();

    }

Installer:

    public abstract class SceneInstaller : MonoBehaviour
    {
        private void Awake() => InstallBindings();
        protected abstract void InstallBindings();

    }
public class LevelInstaller : SceneInstaller
{


}

Using

Bind:

    [SerializeField] private PlayerController _player;

    protected override void InstallBindings()
    {

        DIContainer.Bind(_player);

    }

Reference:

public class Test
{

    private DIReference<PlayerController> _player;

    private void Foo()
    {

        _player.Reference.AAAAAAAAAAAAA();

    }

}
\$\endgroup\$
1
\$\begingroup\$

What you're actually doing is implementing Service Locator which is quite opposite to dependency injection. Please refer to [THIS] discussion about it.

Please use already existing solution.

Few points from my side, just to point out why I think that's wrong for your case:

  1. Why do you think you need more lightweight DI container? Have you performed benchmarks and they proved that your bottle neck is DI injection?
  2. Why have you choosen struct for DIReference? Struct is not good choice for a field, because you're unintenionally (I presume) allocating it on heap. Please refer to [THIS] discussion.
  3. Why would you need DIReference anyway? It's only complicating your code. You don't need to store it as field anywhere - instances that you need are already kept in _references in DIContainer.
  4. Just to point out, as your _references is static readonly field, then this Dictionary is singleton. It causes few potential problems, to name two:
    • Is it guaranteed that every time you install bindings there won't be any other threads that trying to do same thing?
    • Every instance in this dictionary is also singleton-ish. What I mean by that - as soon as first class will install PlayerController into your _references in DIContainer then it'll be available for all classes that uses your DIContainer class. Yet you can still overwrite it. Why would that be a problem? It could cause huge GC pressure, because you'll constantly overwriting PlayerController in your dictionary and GC will have to clean that up which could lead to stopping all threads from running. You definitely want to avoid that especially in games.

I want to strongly suggest to use already existing solution for DI. If you're worried about performance please refer to [THIS] benchamrks.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.