You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF中Unity容器单例VoiceService被重复实例化问题排查

问题分析与解决方案

核心原因

你遇到的VoiceService被实例化两次的问题,主要来自三个冲突点:

  1. 手动实例化与容器管理的双重实例
    你在MainWindow.xaml.cs中手动new VoiceService()并传入MainViewModel,但如果子ViewModel(ViewModelA/B/C)是通过Unity容器创建的,Unity会按照默认的瞬态生命周期(每次Resolve新建实例)创建另一个VoiceService实例,导致两个完全独立的实例同时存在。

  2. Unity注册未指定单例生命周期
    当前Unity注册container.RegisterType<IVoiceService, VoiceService>()是默认的瞬态模式,容器不会保证实例唯一性;同时你自己实现的Lazy单例逻辑,Unity完全不知情——它会直接通过反射调用构造函数创建新实例,绕开你的GetInstance方法。

  3. 自身单例实现的逻辑冲突
    你在事件处理方法(StartedSpeaking/FinishedSpeaking)中调用this.GetInstance()修改单例的状态,但如果是Unity创建的实例,this指向的是Unity实例,而GetInstance()返回的是Lazy单例,会导致状态不一致的隐性bug。


解决方案

方案一:统一用Unity管理单例(推荐)

完全依赖Unity的生命周期管理,去掉自身的Lazy单例实现,避免双重单例逻辑冲突:

  1. 修改Unity注册为单例
    在InitContainer方法中,为IVoiceService指定ContainerControlledLifetimeManager(容器级单例):

    private void InitContainer(IUnityContainer container)
    {
        container.RegisterType<IMainViewModel, MainViewModel>();
        // 注册为容器单例,保证整个容器内只有一个实例
        container.RegisterType<IVoiceService, VoiceService>(new ContainerControlledLifetimeManager());
        container.RegisterType<IAutoMapperService, AutoMapperService>();
    }
    
  2. 移除手动实例化,改用容器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>();
    
  3. 清理自身单例代码(可选)
    既然用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直接返回你的单例实例,避免重复创建:

  1. 修改Unity注册为实例注册
    在InitContainer中,注册已有的单例实例:

    private void InitContainer(IUnityContainer container)
    {
        container.RegisterType<IMainViewModel, MainViewModel>();
        // 注册单例实例,Unity会直接返回这个实例
        container.RegisterInstance<IVoiceService>(VoiceService.GetInstance());
        container.RegisterType<IAutoMapperService, AutoMapperService>();
    }
    
  2. 统一通过容器获取实例
    同样需要修改MainWindow.xaml.cs,改用容器解析IMainViewModel,避免手动创建新的VoiceService实例。


额外注意事项

  • 确保所有依赖IVoiceService的类(包括MainViewModel和子ViewModel)都通过Unity容器创建,不要手动实例化,否则仍会出现多实例问题。
  • VoiceService构造函数中初始化了定时器,多实例会导致多个定时器同时运行,这也是你能感知到实例重复的直接表现,解决单例后即可恢复正常。

内容的提问来源于stack exchange,提问作者Keith POON

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:30:07