WPF中Unity容器单例VoiceService被重复实例化问题排查
核心原因
你遇到的VoiceService被实例化两次的问题,主要来自三个冲突点:
手动实例化与容器管理的双重实例
你在MainWindow.xaml.cs中手动new VoiceService()并传入MainViewModel,但如果子ViewModel(ViewModelA/B/C)是通过Unity容器创建的,Unity会按照默认的瞬态生命周期(每次Resolve新建实例)创建另一个VoiceService实例,导致两个完全独立的实例同时存在。Unity注册未指定单例生命周期
当前Unity注册container.RegisterType<IVoiceService, VoiceService>()是默认的瞬态模式,容器不会保证实例唯一性;同时你自己实现的Lazy单例逻辑,Unity完全不知情——它会直接通过反射调用构造函数创建新实例,绕开你的GetInstance方法。自身单例实现的逻辑冲突
你在事件处理方法(StartedSpeaking/FinishedSpeaking)中调用this.GetInstance()修改单例的状态,但如果是Unity创建的实例,this指向的是Unity实例,而GetInstance()返回的是Lazy单例,会导致状态不一致的隐性bug。
解决方案
方案一:统一用Unity管理单例(推荐)
完全依赖Unity的生命周期管理,去掉自身的Lazy单例实现,避免双重单例逻辑冲突:
修改Unity注册为单例
在InitContainer方法中,为IVoiceService指定ContainerControlledLifetimeManager(容器级单例):private void InitContainer(IUnityContainer container) { container.RegisterType<IMainViewModel, MainViewModel>(); // 注册为容器单例,保证整个容器内只有一个实例 container.RegisterType<IVoiceService, VoiceService>(new ContainerControlledLifetimeManager()); container.RegisterType<IAutoMapperService, AutoMapperService>(); }移除手动实例化,改用容器Resolve
注释掉MainWindow.xaml.cs中手动创建实例的代码,改用Unity解析IMainViewModel:// MainWindow.xaml.cs // VoiceService voiceService = new VoiceService(); // AutoMapperService autoMapperService = new AutoMapperService(); // this.DataContext = new MainViewModel(voiceService, autoMapperService); this.DataContext = _container.Resolve<IMainViewModel>();清理自身单例代码(可选)
既然用Unity管理单例,可移除VoiceService中的Lazy单例相关代码,简化实现:// 移除以下代码 // private static readonly Lazy<VoiceService> lazyAnnounceService = new Lazy<VoiceService>(() => new VoiceService()); // public VoiceService GetInstance() // { // return lazyAnnounceService.Value; // } // 同时修改事件处理方法中的this.GetInstance()为this private void StartedSpeaking(object sender, SpeakStartedEventArgs e) { this._isCurrentlySpeaking = true; } private void FinishedSpeaking(object sender, SpeakCompletedEventArgs e) { this._isCurrentlySpeaking = false; }
方案二:保留自身Lazy单例,让Unity复用该实例
如果你想保留自己的单例实现,可让Unity直接返回你的单例实例,避免重复创建:
修改Unity注册为实例注册
在InitContainer中,注册已有的单例实例:private void InitContainer(IUnityContainer container) { container.RegisterType<IMainViewModel, MainViewModel>(); // 注册单例实例,Unity会直接返回这个实例 container.RegisterInstance<IVoiceService>(VoiceService.GetInstance()); container.RegisterType<IAutoMapperService, AutoMapperService>(); }统一通过容器获取实例
同样需要修改MainWindow.xaml.cs,改用容器解析IMainViewModel,避免手动创建新的VoiceService实例。
额外注意事项
- 确保所有依赖
IVoiceService的类(包括MainViewModel和子ViewModel)都通过Unity容器创建,不要手动实例化,否则仍会出现多实例问题。 VoiceService构造函数中初始化了定时器,多实例会导致多个定时器同时运行,这也是你能感知到实例重复的直接表现,解决单例后即可恢复正常。
内容的提问来源于stack exchange,提问作者Keith POON

