Extenject架构:如何正确实现ProjectContext访问SceneContext服务?
Extenject(Zenject)跨上下文状态访问规范方案
针对ProjectContext中GameStateMachine的LoadGameState需要访问SceneContext内PlayerChooseService的场景,以下是符合Zenject架构规范的实现方案:
方案1:接口抽象+跨上下文实例注册(适合需要持续访问服务的场景)
- 定义抽象接口
IPlayerChoiceProvider,仅暴露LoadGameState所需的方法:public interface IPlayerChoiceProvider { List<string> GetSelectedResourcePaths(); void ClearSelectedResources(); } - 让
PlayerChooseService实现该接口:public class PlayerChooseService : IPlayerChoiceProvider { private List<string> _selectedResources = new List<string>(); public List<string> GetSelectedResourcePaths() => _selectedResources; public void ClearSelectedResources() => _selectedResources.Clear(); // 其他业务方法保持私有或内部可见 } - 在SceneContext的Installer中,将服务实例注册到ProjectContext的全局容器(确保场景卸载时清理):
public class SceneInstaller : MonoInstaller { public override void InstallBindings() { var playerChoiceService = Container.Bind<PlayerChooseService>().AsSingle().NonLazy().Instance; // 将实例注册到ProjectContext的全局容器 ProjectContext.Instance.Container.Bind<IPlayerChoiceProvider>() .FromInstance(playerChoiceService) .AsCached() .WhenInjectedInto<LoadGameState>(); // 场景卸载时解绑,避免内存泄漏 Container.Bind<SceneUninstallHandler>().AsSingle().NonLazy(); } } public class SceneUninstallHandler : IInitializable, IDisposable { private readonly IContainer _projectContainer; private readonly IPlayerChoiceProvider _playerChoiceProvider; public SceneUninstallHandler(IContainer projectContainer, IPlayerChoiceProvider playerChoiceProvider) { _projectContainer = projectContainer; _playerChoiceProvider = playerChoiceProvider; } public void Initialize() {} public void Dispose() { _projectContainer.Unbind<IPlayerChoiceProvider>(_playerChoiceProvider); } } - 在
LoadGameState中直接注入接口实例:public class LoadGameState : GameState { [Inject] private IPlayerChoiceProvider _playerChoiceProvider; public override void Enter() { var selectedPaths = _playerChoiceProvider.GetSelectedResourcePaths(); // 执行资源加载逻辑 } }
方案2:SignalBus数据传递(适合一次性数据传递场景)
- 定义携带玩家选择数据的信号:
public class PlayerChoiceConfirmedSignal : Signal { public List<string> SelectedResourcePaths; } - 在ProjectContext的Installer中绑定SignalBus:
public class ProjectInstaller : MonoInstaller { public override void InstallBindings() { Container.Bind<SignalBus>().AsSingle(); Container.DeclareSignal<PlayerChoiceConfirmedSignal>(); } } - 在
PlayerChooseService中发送信号(玩家选择完成时):public class PlayerChooseService { [Inject] private SignalBus _signalBus; private List<string> _selectedResources = new List<string>(); public void OnPlayerConfirmedChoice() { _signalBus.Fire(new PlayerChoiceConfirmedSignal { SelectedResourcePaths = _selectedResources }); } } - 在
LoadGameState中订阅信号并缓存数据:public class LoadGameState : GameState, IDisposable { [Inject] private SignalBus _signalBus; private List<string> _cachedSelectedPaths; private SignalSubscription _subscription; public override void Enter() { _subscription = _signalBus.Subscribe<PlayerChoiceConfirmedSignal>(OnChoiceConfirmed); // 如果信号已发送,直接使用缓存数据执行加载 if (_cachedSelectedPaths != null) { LoadSelectedResources(_cachedSelectedPaths); } } private void OnChoiceConfirmed(PlayerChoiceConfirmedSignal signal) { _cachedSelectedPaths = signal.SelectedResourcePaths; LoadSelectedResources(_cachedSelectedPaths); } private void LoadSelectedResources(List<string> paths) { // 资源加载逻辑 } public void Dispose() { _subscription?.Dispose(); } }
方案3:状态初始化注入(适合场景与状态强绑定的场景)
- 在
LoadGameState中添加初始化方法:public class LoadGameState : GameState { private IPlayerChoiceProvider _playerChoiceProvider; public void Initialize(IPlayerChoiceProvider choiceProvider) { _playerChoiceProvider = choiceProvider; } public override void Enter() { var selectedPaths = _playerChoiceProvider.GetSelectedResourcePaths(); // 加载逻辑 } } - 在SceneContext的Installer中获取状态实例并注入依赖:
public class SceneInstaller : MonoInstaller { public override void InstallBindings() { var playerChoiceService = Container.Bind<PlayerChooseService>().AsSingle().NonLazy().Instance; // 从ProjectContext获取GameStateMachine及LoadGameState var gameStateMachine = ProjectContext.Instance.Container.Resolve<GameStateMachine>(); var loadGameState = gameStateMachine.GetState<LoadGameState>(); loadGameState.Initialize(playerChoiceService); } }
规范要点
- 优先使用SignalBus方案,完全解耦上下文,避免跨上下文的实例依赖,符合Zenject的模块化设计原则
- 若需持续访问服务,必须用接口抽象,禁止直接依赖SceneContext中的具体服务类型
- 所有跨上下文的实例引用必须在场景卸载时清理,防止内存泄漏
- 全局上下文(ProjectContext)不应持有场景上下文(SceneContext)的任何类型引用,保持全局逻辑的独立性
内容的提问来源于stack exchange,提问作者Vladyslav Yeromenko
相关产品推荐
相关产品推荐

