记录一次Prism9隐式注册引发的事件聚合器失效问题

直接上代码 1、我的注册从App的RegisterTypes方法迁移到了模块

arduino 复制代码
 public class AccountModule : IModule
 {
     public void OnInitialized(IContainerProvider containerProvider)
     {
     }

     public void RegisterTypes(IContainerRegistry containerRegistry)
     {
         containerRegistry.RegisterSingleton<LoginAccount>();
     }
 }

虽然我是立即执行的引用模块

csharp 复制代码
 protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
 {
     Logger.WriteLocal("开始加载dll");
     Assembly.LoadFrom(@"Music.Core.dll");
     Logger.WriteLocal("开始加载模块");
     moduleCatalog.AddModule<AccountModule>(InitializationMode.WhenAvailable);

     var m = moduleCatalog.Modules.First(me => me.ModuleName == nameof(AccountModule));
     Logger.WriteLocal($"🔍 模块 {m.ModuleName} 状态 = {m.State}");
 }

但是log显示NotStarted 我虽然登录的时候写了

csharp 复制代码
 public class LoginWindowViewModel : BindableBase
 {
     private readonly LoginAccount _loginAccount;

     public LoginWindowViewModel(IEventAggregator eventAggregator, LoginAccount loginAccount)
     {
         _eventAggregator = eventAggregator;
         LoginCommand = new DelegateCommand<Window>(ExecuteLogin);

     }

     private IEventAggregator _eventAggregator;

     private void ExecuteLogin(Window window)
     {
         _logger.WriteLocal("🚀 准备发布 LoginEvent");
         _eventAggregator.GetEvent<LoginEvent>().Publish(window);
     }

     public ICommand LoginCommand { get; set; }
 }

但是事件聚合器

csharp 复制代码
 public class LoginAccount
 {
     private readonly IEventAggregator _eventAggregator;
     private ITangdaoLogger _logger = TangdaoLogger.Get(typeof(LoginAccount));

     public LoginAccount(IEventAggregator eventAggregator)
     {
         _eventAggregator = eventAggregator;
         var token = _eventAggregator.GetEvent<LoginEvent>().Subscribe(Login);
         _logger.WriteLocal($"✅ LoginAccount 订阅完成,token={token.GetHashCode()}");
     }

     private void Login(Window window)
     {
         window.DialogResult = true;
     }
 }

并没有进入 因为隐式注册的时候LoginAccount的生命周期是短暂的或者没有传递,此时,我改为

ini 复制代码
  private readonly LoginAccount _loginAccount;

  public LoginWindowViewModel(IEventAggregator eventAggregator, LoginAccount loginAccount)
  {
      _eventAggregator = eventAggregator;
       _loginAccount = loginAccount;
      LoginCommand = new DelegateCommand<Window>(ExecuteLogin);
  }

我只是引入了字段,现在成功了 说明 1、可能被GC回收了 2、Prism默认的解析机制,与微软的ServiceCollection不同,ServiceCollection是注册了类才能解析,而Prism具有隐式注册机制,比如程序启动的时候

kotlin 复制代码
 return Container.Resolve<MainWindow>();

我们明明没有对MainWindow进行注册,但是却可以解析出来,而ServiceCollection不可以, 这个隐式注册的生命周期是瞬态的,导致拿到的不是同一个 我继续尝试,使用Rgister注册,结果是只要使用了 _loginAccount=loginAccount成功,不使用失败 那么现在结论只剩下一个了, 我现在将事件聚合器改为强引用

swift 复制代码
_eventAggregator.GetEvent<LoginEvent>().Subscribe(Login, ThreadOption.UIThread, true);

不再使用字段长时间保持生命周期的引用,结果登录成功 我接着尝试,连同构造器的引用去除,结果登录失败,说明虽然不需要写字段,使用了强制引用,但是依然需要在构造器写进对象

java 复制代码
public LoginWindowViewModel(IEventAggregator eventAggregator, LoginAccount loginAccount)

说明

arduino 复制代码
public LoginWindowViewModel(IEventAggregator eventAggregator, LoginAccount loginAccount)
{
    _eventAggregator = eventAggregator;
    
    // 情况A:参数未被使用
    // JIT可能认为loginAccount是栈上的临时变量,方法结束后即可回收
    
    // 情况B:保存到字段
    _loginAccount = loginAccount; // 现在引用进入GC Roots链
}

JIT可能将未使用的参数优化掉 实际的执行代码可能根本不保留这个引用 GC Roots的定义: 静态字段 活动线程的栈帧中的局部变量 CPU寄存器中的引用 实例字段(当实例本身被根引用时) 关键的区别在于

arduino 复制代码
// 栈上引用(可能被提前回收)
public void Method(LoginAccount param)
{
    // param 只在栈上,方法结束后GC可能回收
}

// 实例字段引用(稳定的GC Root)  
public class ViewModel
{
    private LoginAccount _account; // 实例字段,生命周期与ViewModel一致
}
相关推荐
Moment5 分钟前
想要长期陪伴你的助理?先从部署一个 OpenClaw 开始 😍😍😍
前端·后端·github
Das1_7 分钟前
【Golang 数据结构】Slice 底层机制
后端·go
得物技术7 分钟前
深入剖析Spark UI界面:参数与界面详解|得物技术
大数据·后端·spark
古时的风筝9 分钟前
花10 分钟时间,把终端改造成“生产力武器”:Ghostty + Yazi + Lazygit 配置全流程
前端·后端·程序员
Cache技术分享10 分钟前
340. Java Stream API - 理解并行流的额外开销
前端·后端
初次攀爬者12 分钟前
RocketMQ 消息可靠性保障与堆积处理
后端·消息队列·rocketmq
ygxb18 分钟前
如何去创建一个规范化的Agent SKIll?
后端·ai编程·claude
JxWang051 小时前
Task01:环境搭建,初识数据库
后端
周杰伦jc1 小时前
RocketMQ 完全指南:从入门到原理到生产实战、八股面试
后端
小码哥_常1 小时前
Java可执行JAR包打包大揭秘:三种方式全解析
后端