记录一次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一致
}

浙公网安备 33010602011771号