我对 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
.
强制引用类型
不能重载点运算符;但是,您可以重载 explicit
和 implicit
运算符:
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;
我决定制作自己的轻型 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
. 强制引用类型
不能重载点运算符;但是,您可以重载
explicit
和implicit
运算符: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;