实现状态模式时检测到System.InvalidOperationException循环依赖问题
解决C#状态模式中水三态的依赖注入循环依赖问题
问题原因
你的代码中GasState构造函数依赖ILiquidState,而LiquidState又依赖IGasState,形成了循环依赖链(IGasState → ILiquidState → IGasState),导致依赖注入容器无法解析这些服务实例。
解决方案:使用状态工厂延迟获取实例
最合理的修复方式是引入状态工厂,让每个状态通过工厂获取目标转换状态,而不是直接注入互相依赖的实例。这种方式既打破循环依赖,又保持状态模式的封装性。
步骤1:定义状态工厂接口与实现
namespace LibraryExamples.Water { public interface IWaterStateFactory { T GetState<T>() where T : IWaterState; } public class WaterStateFactory : IWaterStateFactory { private readonly IServiceProvider _serviceProvider; public WaterStateFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public T GetState<T>() where T : IWaterState { return _serviceProvider.GetRequiredService<T>(); } } }
步骤2:重构所有状态类,依赖工厂而非直接依赖其他状态
修改GasState
namespace LibraryExamples.Water.States { public class GasState : IGasState { private readonly IWaterStateFactory _stateFactory; public GasState(IWaterStateFactory stateFactory) { _stateFactory = stateFactory; } public IWaterState Heat() { Console.WriteLine("Already in a gaseous state. Heating has no effect."); return this; } public IWaterState Cool() { Console.WriteLine("Cooling gas (steam) turns it into liquid water."); return _stateFactory.GetState<ILiquidState>(); } public void Describe() { Console.WriteLine("Current state: Gas"); } public ILiquidState ToLiquidState() { return _stateFactory.GetState<ILiquidState>(); } } }
修改LiquidState
namespace LibraryExamples.Water.States { public class LiquidState : ILiquidState { private readonly IWaterStateFactory _stateFactory; public LiquidState(IWaterStateFactory stateFactory) { _stateFactory = stateFactory; } public IWaterState Cool() { Console.WriteLine("Cooling liquid water turns it into solid water."); return _stateFactory.GetState<ISolideState>(); } public IWaterState Heat() { Console.WriteLine("Heating liquid water turns into gas water"); return _stateFactory.GetState<IGasState>(); } public void Describe() { Console.WriteLine("Current state: Liquid"); // 修复原代码的状态描述错误 } public IGasState ToGasState() { return _stateFactory.GetState<IGasState>(); } public ISolideState ToSolidState() { return _stateFactory.GetState<ISolideState>(); } } }
修改SolidState
namespace LibraryExamples.Water.States { public class SolidState : ISolideState { private readonly IWaterStateFactory _stateFactory; public SolidState(IWaterStateFactory stateFactory) { _stateFactory = stateFactory; } public IWaterState Heat() { Console.WriteLine("Heating solid water turns it into liquid water."); return _stateFactory.GetState<ILiquidState>(); } public IWaterState Cool() { Console.WriteLine("Already solid. Cooling has no effect."); return this; } public void Describe() { Console.WriteLine("Current state: Solid"); } public ILiquidState ToLiquidState() { return _stateFactory.GetState<ILiquidState>(); } } }
步骤3:在依赖注入容器中注册服务
// 注册工厂 services.AddScoped<IWaterStateFactory, WaterStateFactory>(); // 注册所有状态 services.AddScoped<IGasState, GasState>(); services.AddScoped<ILiquidState, LiquidState>(); services.AddScoped<ISolideState, SolidState>(); // 注册Water,指定初始状态(这里以液态为例) services.AddScoped<IWater, Water>(sp => new Water(sp.GetRequiredService<ILiquidState>()) );
替代方案:属性注入(不推荐)
如果不想引入工厂,可以使用属性注入来打破循环,但这种方式会隐藏依赖关系,不利于代码维护。示例如下(以ASP.NET Core为例):
public class GasState : IGasState { // 标记为可注入属性 public ILiquidState LiquidState { get; set; } // 提供无参构造函数 public GasState() {} // 其他方法保持不变 }
需要在DI容器中配置允许属性注入:
services.AddScoped<IGasState, GasState>(sp => { var state = new GasState(); state.LiquidState = sp.GetRequiredService<ILiquidState>(); return state; }); // 同理配置LiquidState
总结
推荐使用状态工厂模式,它不仅解决了循环依赖问题,还让状态转换逻辑更灵活,依赖关系清晰可见,符合依赖注入的最佳实践。
内容的提问来源于stack exchange,提问作者S. LaBani
相关产品推荐
相关产品推荐

