在 ProjectContext 上使用 Zenject FromSubContainerResolve 到 SceneContext

Use Zenject FromSubContainerResolve on ProjectContext to SceneContext

我有一个 Unity + Zenject 设置,其中 ProjectInstaller 具有一些遵循“模态”界面的全局依赖项,例如,

public class ProjectInstaller : MonoInstaller {
  public override void InstallBindings() {
    Container.Bind<ModalManager>().AsSingle();

    Container.Bind<Modal>().To<DialogManager>().AsSingle();
  }
}

有些模态仅与某些场景相关,所以我将它们绑定在 SceneInstaller:

public class SceneInstaller : MonoInstaller {
  public override void InstallBindings() {
    Container.BindInterfacesAndSelfTo<InventoryManager>()
      .FromComponentInNewPrefab(InventoryPrefab)
      .AsSingle()
  }
}

我想从单个 ModalManager 管理 所有 模态,在项目范围内定义。所以它有一个 List<Modal> 绑定:

public class ModalManager : MonoBehaviour {
    [Inject]
    protected List<Modal> _modals;
}

当我 运行 这样做时,ModalManager 只得到一个模态:在项目范围内定义的模态。在我的理解中,SceneContextProjectContext 的子容器。所以我应该可以在 ProjectInstaller 中使用 FromSubContainerResolve 来绑定子场景中的项目,也许可以添加一行:

// ProjectInstaller.cs
public override void InstallBindings() {
  // ...
  Container.Bind<Modal>().To<InventoryManager>().FromSubContainerResolve();
}

但我不确定这 11 种 FromSubContainerResolve 方法中的哪一种对这种情况有意义。它们似乎都与具有游戏对象上下文的预制件相关, 用于 ProjectContext.

这个用例有意义吗?有没有更简单或更好的方法?

问题是 ModalManager 只能注入直接添加到 ProjectContext 的依赖项。对于这类问题,我建议使用以下模式:

public interface IModal
{
}

public class ModalManager
{
    private readonly List<IModal> _modals = new List<IModal>();

    public IReadOnlyList<IModal> Modals
    {
        get { return _modals; }
    }

    public void AddModal(IModal modal)
    {
        _modals.Add(modal);
    }

    public bool RemoveModal(IModal modal)
    {
        return _modals.Remove(modal);
    }
}

public class ModalRegisterHandler : IInitializable, IDisposable
{
    private readonly List<IModal> _modals;
    private readonly ModalManager _modalManager;

    public ModalRegisterHandler(
        // We need to use InjectSources.Local here, otherwise we will
        // add any project context modals again in each scene
        [Inject(Source = InjectSources.Local)]
        List<IModal> modals, ModalManager modalManager)
    {
        _modals = modals;
        _modalManager = modalManager;
    }

    public void Initialize()
    {
        foreach (var modal in _modals)
        {
            _modalManager.AddModal(modal);
        }
    }

    public void Dispose()
    {
        // We don't want ModalManager to retain references to Modals defined in unloaded scenes
        // (dispose is executed on scene unload)
        foreach (var modal in _modals)
        {
            _modalManager.RemoveModal(modal);
        }
    }
}

public class SceneInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<IModal>().To<FooModal>();
        Container.Bind<IModal>().To<BarModal>();
    }
}

public class ProjectInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        // We use CopyIntoDirectSubContainers so that ModalRegisterHandler gets automatically added to every 
        // scene context
        Container.BindInterfacesTo<ModalRegisterHandler>().AsSingle().CopyIntoDirectSubContainers();

        Container.Bind<ModalManager>().AsSingle();

        Container.Bind<IModal>().To<QuxModal>();
        Container.Bind<IModal>().To<FizzModal>();
    }
}