我对 DI 容器的简单实现 [打开]

My simple implementation of a DI container [open]

我决定制作自己的轻型 DI 容器。

我知道 Zenject 等。我想要一个非常轻的模拟。

我担心一些事情:

1) 在这种情况下使用结构而不是 class 作为参考是否正确?

2)每次访问_player.Reference时都会检查null(在UnityEngine.After中销毁Object不等于null,所以使用SafeIsUnityNull)

3) 双点。我可以重载运算符,以便代替 _player.Reference.AAAAAAAAAAAAA();,它可以工作 _player.AAAAAAAAAAAAA(); ??

容器本身:

    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)}");

        }

    }

现场参考:

    public struct DIReference<T> where T : Object
    {

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

    }

安装人员:

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

    }
public class LevelInstaller : SceneInstaller
{


}

使用

绑定:

    [SerializeField] private PlayerController _player;

    protected override void InstallBindings()
    {

        DIContainer.Bind(_player);

    }

参考:

public class Test
{

    private DIReference<PlayerController> _player;

    private void Foo()
    {

        _player.Reference.AAAAAAAAAAAAA();

    }

}

几点:

  • 您不能将约束指定为 where T : Object,因为每个类型都直接或间接派生自 System.Object;但是,您可以使用 where T : class.

  • 强制引用类型
  • 不能重载点运算符;但是,您可以重载 explicitimplicit 运算符:

    public struct DIReference<T> where T : class
    {
        private T _reference;
        public T Reference => !_reference.SafeIsUnityNull()
            ? _reference
            : _reference = DIContainer.Resolve<T>();
    
        public static implicit operator T(DIReference<T> reference) => reference._reference;
    }
    

    这使您可以执行以下操作

    _player.Reference.AAAAAAAAAAAAA();
    
    // Implicitly convert from DIReference<PlayerController> to PlayerController:
    PlayerController pc = _player; 
    pc.AAAAAAAAAAAAA();
    
    //or
    
    ((PlayerController)_player).AAAAAAAAAAAAA();
    

    通过重载 explicit,您被迫使用最后一个变体。

  • 可变结构很讨厌。您的结构是只读的但不是不可变的。从集合中访问时,您会得到原始结构的副本,并且对此副本的所有更改都不会写回集合中的原始值。

    让我们假设您有 List<DIReference<T>> refList。如果现在你写 var ref = refList[i].Reference; 那么新解析的引用将不会存储回 refList!。你必须写:

    var diRef = refList[i];
    var ref = diRef.Reference; // May resolve the _reference field from DIContainer!
    refList[i] = diRef;