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

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

 public class AccountModule : IModule
 {
     public void OnInitialized(IContainerProvider containerProvider)
     {
     }

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

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

 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
我虽然登录的时候写了

 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; }
 }

但是事件聚合器

 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的生命周期是短暂的或者没有传递,此时,我改为

  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具有隐式注册机制,比如程序启动的时候

 return Container.Resolve<MainWindow>();

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

_eventAggregator.GetEvent<LoginEvent>().Subscribe(Login, ThreadOption.UIThread, true);

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

public LoginWindowViewModel(IEventAggregator eventAggregator, LoginAccount loginAccount)

说明

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

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

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

// 实例字段引用(稳定的GC Root)  
public class ViewModel
{
    private LoginAccount _account; // 实例字段,生命周期与ViewModel一致
}
posted @ 2025-11-06 01:45  孤沉  阅读(18)  评论(0)    收藏  举报