发布和订阅消息

重要

MessagingCenter 在 .NET 10 中已弃用。 建议从 CommunityToolkit.Mvvm 包迁移到WeakReferenceMessenger,或使用具有简单方案弱引用的事件。 有关详细信息,请参阅 Messenger

MessagingCenter 是一种方便的 API,用于实现发布-订阅模式,而无需在发布服务器和订阅服务器之间直接引用。 在 .NET 10 及更高版本中,应首选以下项之一:

  • 用于分离、可测试的基于消息的通信的 CommunityToolkit.Mvvm WeakReferenceMessenger
  • 用于本地通信的常规 .NET 事件,最好是 WeakEventManager 避免内存泄漏。

迁移到 WeakReferenceMessenger

Toolkit messenger 使用强类型邮件和对收件人的弱引用。 最少的迁移如下所示:

  1. 将 CommunityToolkit.Mvvm 包添加到项目。 在项目文件中(.csproj):

    <ItemGroup>
      <PackageReference Include="CommunityToolkit.Mvvm" Version="8.*" />
    </ItemGroup>
    
  2. 定义消息类型(可选)发送单个有效负载值时使用 ValueChangedMessage<T> ):

    using CommunityToolkit.Mvvm.Messaging.Messages;
    
    public sealed class HiMessage : ValueChangedMessage<string>
    {
        public HiMessage(string name) : base(name) { }
    }
    
  3. 从发布者发送消息:

    using CommunityToolkit.Mvvm.Messaging;
    
    // Send without a direct reference to any recipients
    WeakReferenceMessenger.Default.Send(new HiMessage("John"));
    
  4. 接收收件人中的邮件:

    • 实现 IRecipient<TMessage> 并注册信使,或
    • 继承 ObservableRecipient 并启用接收。
    using CommunityToolkit.Mvvm.Messaging;
    using CommunityToolkit.Mvvm.Messaging.Messages;
    
    public sealed class GreetingViewModel : IRecipient<HiMessage>
    {
        public GreetingViewModel()
        {
            // Register this instance to receive HiMessage notifications
            WeakReferenceMessenger.Default.Register(this);
        }
    
        public void Receive(HiMessage message)
        {
            var name = message.Value; // "John"
            // Handle the message (e.g., update state)
        }
    
        // Call this when the recipient is disposed/no longer needed
        public void Unregister()
        {
            WeakReferenceMessenger.Default.Unregister<HiMessage>(this);
        }
    }
    

映射常见的 MessagingCenter 模式

  • 无有效负载发送: MessagingCenter.Send<TSender>(sender, message) →定义空消息类型(无有效负载)和 Send(new MyMessage())
  • 使用有效负载发送: MessagingCenter.Send<TSender, TArgs>(sender, message, args) →使用 ValueChangedMessage<TArgs> 或具有属性的自定义消息。
  • 订阅回调: MessagingCenter.Subscribe<TSender>(subscriber, message, callback) →实现 IRecipient<TMessage> 和注册;方法是 Receive 回调。
  • 取消订阅: MessagingCenter.UnsubscribeWeakReferenceMessenger.Default.Unregister<TMessage>(recipient)

何时改用事件

对于简单的本地通信(例如,控件与其父级之间),标准 .NET 事件通常足够。 若要降低长期发布者内存泄漏的风险,请使用 WeakEventManager

public class Counter
{
    readonly Microsoft.Maui.WeakEventManager _weakEventManager = new();

    public event EventHandler<int> CountChanged
    {
        add => _weakEventManager.AddEventHandler(value);
        remove => _weakEventManager.RemoveEventHandler(value);
    }

    int _count;
    public void Increment()
    {
        _count++;
        _weakEventManager.HandleEvent(this, _count, nameof(CountChanged));
    }
}

小窍门

如果已在 CommunityToolkit 中使用 MVVM, ObservableRecipient 则可以在设置时 IsActive 自动管理注册生存期。

发布-订阅模式是一种消息传送模式,发布者无需了解任何接收方(称为订阅者)即可发送消息。 同样,订阅者无需了解任何发布者即可侦听特定消息。

.NET 中的事件实现发布-订阅模式,如果不需要松散耦合(例如控件和包含它的页面),则组件之间的通信层是最简单且最简单的方法。 但是,发布服务器和订阅服务器生存期由对象相互引用耦合,订阅服务器类型必须具有对发布服务器的类型的引用。 这可以创建内存管理问题,尤其是在存在订阅静态或长生存期对象的事件的短生存期对象时。 如果未删除事件处理程序,则订阅服务器将通过对它的引用在发布服务器中保持活动状态,这将阻止或延迟订阅服务器的垃圾回收。

.NET 多平台应用 UI (.NET MAUI) MessagingCenter 类实现发布-订阅模式,允许不方便按对象和类型引用链接的组件之间基于消息的通信。 此机制允许发布者和订阅者在不相互引用的情况下进行通信,从而帮助减少它们之间的依赖关系。

重要

MessagingCenter 已弃用,建议将其 WeakReferenceMessenger 替换为 CommunityToolkit.Mvvm NuGet 包。 有关详细信息,请参阅 Messenger

MessagingCenter 类提供多播发布-订阅功能。 这意味着,可以有多个发布者发布单个消息,并且可以有多个订阅者侦听同一消息:

多播发布-订阅功能。

发布者使用 MessagingCenter.Send 该方法发送消息,而订阅者使用 MessagingCenter.Subscribe 该方法侦听消息。 此外,订阅者还可以使用该方法从消息订阅取消订阅( MessagingCenter.Unsubscribe 如果需要)。

重要

在内部,类 MessagingCenter 使用弱引用。 这意味着它不会使对象保持活动状态,并允许对其进行垃圾回收。 因此,仅当类不再希望接收消息时,才应该取消订阅消息。

发布消息

MessagingCenter 消息是字符串。 发布者使用其中一个 MessagingCenter.Send 重载通知订阅者消息。 以下代码示例发布消息 Hi

MessagingCenter.Send<MainPage>(this, "Hi");

在此示例中, Send 该方法指定表示发送方的泛型参数。 若要接收消息,订阅服务器还必须指定相同的泛型参数,指示他们正在侦听来自该发件人的消息。 此外,此示例还指定两个方法参数:

  • 第一个参数指定发送方实例。
  • 第二个参数指定消息。

还可以使用消息发送有效负载数据:

MessagingCenter.Send<MainPage, string>(this, "Hi", "John");

在此示例中,该方法 Send 指定两个泛型参数。 第一个是发送消息的类型,第二种是要发送的有效负载数据的类型。 若要接收消息,订阅服务器还必须指定相同的泛型参数。 这允许多个消息共享消息标识,但发送不同的有效负载数据类型,供不同的订阅者接收。 此外,此示例还指定第三个方法参数,该参数表示要发送到订阅服务器的有效负载数据。 在这种情况下,有效负载数据是一个 string

该方法 Send 将使用火忘方法发布消息和任何有效负载数据。 因此,即使没有注册用于接收消息的订阅者,也会发送消息。 在这种情况下,将忽略已发送的消息。

订阅消息

订阅者可以使用其中一个 MessagingCenter.Subscribe 重载注册以接收消息。 下面的代码示例演示了一个示例:

MessagingCenter.Subscribe<MainPage> (this, "Hi", (sender) =>
{
    // Do something whenever the "Hi" message is received
});

在此示例中,该方法 Subscribethis 对象订阅类型 Hi 发送 MainPage 的消息,并执行回调委托以响应接收消息。 指定为 lambda 表达式的回调委托可以是更新 UI、保存某些数据或触发其他作的代码。

注释

订阅服务器可能不需要处理已发布消息的每个实例,这可以由方法上 Subscribe 指定的泛型类型参数控制。

以下示例演示如何订阅包含有效负载数据的消息:

MessagingCenter.Subscribe<MainPage, string>(this, "Hi", async (sender, arg) =>
{
    await DisplayAlert("Message received", "arg=" + arg, "OK");
});

在此示例中,该方法 Subscribe 订阅 Hi 类型 MainPage 发送的消息,其有效负载数据是一个 string。 回调委托是在响应接收此类消息时执行的,该消息在警报中显示有效负载数据。

重要

由该方法执行的 Subscribe 委托将在使用 Send 该方法发布消息的同一线程上执行。

取消订阅邮件

订阅者可以取消订阅他们不再想要接收的消息。 这是通过其中一个重载实现的 MessagingCenter.Unsubscribe

MessagingCenter.Unsubscribe<MainPage>(this, "Hi");

在此示例中,该方法Unsubscribe取消订阅this类型MainPage发送的消息中的Hi对象。

应使用指定两个泛型参数的 Unsubscribe 重载取消订阅包含有效负载数据的消息:

MessagingCenter.Unsubscribe<MainPage, string>(this, "Hi");

在此示例中,该方法Unsubscribe从类型发送MainPage的消息中取消订阅this对象,其有效负载数据是一个 stringHi