
1. 为什么我们需要Messenger消息机制在开发WPF应用时ViewModel之间的通信一直是个让人头疼的问题。想象一下你正在开发一个电商后台管理系统用户管理模块需要通知订单模块刷新数据订单模块又需要触发实时通知模块。如果用传统的事件或直接引用方式代码很快就会变成一团乱麻。我去年接手过一个老项目里面ViewModel之间直接互相引用改一个地方要动全身测试起来更是噩梦。后来用Messenger重构后代码量减少了30%单元测试覆盖率直接从40%提升到85%。这就是解耦的魅力Messenger就像个邮局ViewModel之间不需要知道对方在哪只需要把消息投递到邮局邮局会负责把消息送到正确的收件人手里。CommunityToolkit.Mvvm中的WeakReferenceMessenger更是贴心地使用了弱引用完全不用担心内存泄漏问题。2. Messenger的三种核心用法2.1 基础消息收发最简单的用法就是发送一个值类型消息。比如在用户登录成功后通知其他模块// 发送登录成功消息 WeakReferenceMessenger.Default.Send(UserLoggedIn); // 在需要接收的ViewModel中 public class OrderViewModel : IRecipientstring { public OrderViewModel() { WeakReferenceMessenger.Default.Register(this); } public void Receive(string message) { if(message UserLoggedIn) { // 刷新订单数据 } } }我在实际项目中发现这种简单消息最适合触发全局状态变更。但要注意消息类型不要太泛建议用枚举或特定字符串避免消息冲突。2.2 强类型消息对象当需要传递复杂数据时可以定义专门的消息类。比如订单创建成功后public record OrderCreatedMessage(int OrderId, DateTime CreateTime); // 发送 var message new OrderCreatedMessage(123, DateTime.Now); WeakReferenceMessenger.Default.Send(message); // 接收 public class NotificationViewModel : IRecipientOrderCreatedMessage { public void Receive(OrderCreatedMessage message) { // 发送通知 } }record类型特别适合做消息载体因为它默认就是不可变的而且自带值比较逻辑。我在电商项目中用这种方式处理了20多种业务消息代码非常清晰。2.3 带回调的请求响应模式有时候我们需要等待响应这时候可以用RequestMessage// 请求用户权限 var request new RequestMessagebool(); WeakReferenceMessenger.Default.Send(request); if(request.Response) { // 用户已授权 } // 在权限管理ViewModel中 public class PermissionViewModel : IRecipientRequestMessagebool { public void Receive(RequestMessagebool message) { // 检查权限 message.Reply(HasPermission); } }这个模式特别适合跨模块的权限校验。我在一个金融系统中用这种方式实现了统一权限控制比之前的分散检查要可靠得多。3. 避免内存泄漏的实战技巧WeakReferenceMessenger虽然用了弱引用但用不好还是会出问题。分享几个我踩过的坑3.1 注册与注销的最佳时机最常见的错误是只注册不注销。虽然弱引用能防止内存泄漏但消息处理逻辑会一直存在。正确的做法是在ViewModel的生命周期中管理public class MyViewModel : IDisposable, IRecipientstring { public MyViewModel() { WeakReferenceMessenger.Default.Register(this); } public void Dispose() { WeakReferenceMessenger.Default.Unregisterstring(this); } public void Receive(string message) { ... } }在WPF中可以配合Unloaded事件使用。我在一个项目中曾经因为忘记注销导致页面切换后旧处理器还在响应消息产生了诡异的bug。3.2 消息令牌的正确使用当有多个同类型消息需要区分时可以使用令牌// 发送带令牌的消息 WeakReferenceMessenger.Default.Send(new RefreshMessage(), OrderModule); // 注册时指定相同令牌 WeakReferenceMessenger.Default.RegisterRefreshMessage, string( this, OrderModule, (r, m) { /* 只处理OrderModule的刷新 */ });这个技巧在我开发一个多标签编辑器时特别有用每个标签页都有自己的刷新令牌互不干扰。3.3 处理消息冲突当多个模块发送相同类型消息时建议为每种业务消息创建独立的消息类型而不是都用string。我曾经遇到过两个模块都用Refresh作为消息内容结果产生了意外的耦合。4. 高级应用场景剖析4.1 跨进程消息桥接通过稍微改造Messenger甚至可以用于跨进程通信。我在一个分布式系统中这样实现// 主进程 WeakReferenceMessenger.Default.RegisterCrossProcessMessage(this, async (r, m) { await IPC.SendToSecondaryProcess(m); }); // 子进程 IPC.OnReceived (msg) { WeakReferenceMessenger.Default.Send(msg); };这样所有模块都不需要知道消息是来自本地还是远程实现了完美的透明性。4.2 与MediatR的配合使用在复杂系统中可以结合MediatR使用public class MediatRAdapter : IRecipientIMessage { private readonly IMediator _mediator; public MediatRAdapter(IMediator mediator) { _mediator mediator; WeakReferenceMessenger.Default.Register(this); } public void Receive(IMessage message) { _mediator.Publish(message); } }这种架构下Messenger负责ViewModel间通信MediatR处理业务逻辑各司其职。4.3 性能优化技巧在大规模消息场景下我总结了几点优化经验对高频消息使用struct而不是class为热路径消息实现IEquatable接口批量处理连续的同类型消息避免在消息处理中执行耗时操作在一个人流分析系统中通过这些优化将消息处理吞吐量提升了5倍。5. 测试策略与调试技巧5.1 单元测试模式Messenger的一个巨大优势是便于测试。这是我的典型测试写法[Test] public void Should_Handle_OrderMessage() { // 准备 var messenger new WeakReferenceMessenger(); var vm new OrderViewModel(messenger); var testMessage new OrderMessage(123); // 执行 messenger.Send(testMessage); // 断言 Assert.That(vm.LatestOrderId, Is.EqualTo(123)); }因为不依赖具体实例测试可以非常纯粹。在我的项目中这种模式让ViewModel测试覆盖率轻松达到90%以上。5.2 调试消息流当消息系统复杂时可以添加日志public class LoggingMessenger : IMessenger { private readonly IMessenger _inner; public LoggingMessenger(IMessenger inner) { _inner inner; } public void SendTMessage(TMessage message) { Debug.WriteLine($Sending {typeof(TMessage).Name}); _inner.Send(message); } // 其他方法实现... }这个技巧帮我快速定位过一个消息丢失的问题原来是注册时类型不匹配。5.3 性能监控对于关键业务消息可以添加性能跟踪public class TimedMessageT : T { public DateTime SendTime { get; } DateTime.Now; } // 在处理器中计算延迟 var delay DateTime.Now - message.SendTime; if(delay TimeSpan.FromSeconds(1)) { Logger.Warning(消息处理延迟过高); }在一个实时交易系统中这套机制帮我们发现了几个性能瓶颈。