C#完成一个应用内的消息中心

本文会讲解如何使用 C# 完成一个应用内部的消息中心(事件总线),事件驱动最大的好处就是可以很大程度的解耦合,松散结构。

模式介绍

简单来说就是一个发布订阅模式设计,对于消息的产生者来说,并不对直接将消息发送给消息的处理者,而是将消息发送给独立的消息中心,这里和观察者模式做个区别。对于消息产生者来说,无需知道有哪些订阅者。而对于订阅者来说,同样无需知道消息发布者的存在。

实现

使用 C# 实现如下,一个消息中心有两个方法,一个是发布消息:Send/Publish,有一个是订阅消息:Subscribe

public static class MessageCenter
{
    public static void Send(object sender, string name, object data)
    {
        InnerSend(sender, name, data);
    }
    
     public static void Subscribe<TSender, TData>(object subscriber, string name, Action<TSender, TData> action)
    {
        InnerSubscribe(subscriber, name, action?.Method, action?.Target);
    }
}

接着实现 InnerSend ,发布消息本质就是查询所有的订阅者,并执行订阅的响应事件

private static void InnerSend(object sender, string name, object data)
{
    if (string.IsNullOrEmpty(name))
        throw new NullReferenceException();
    if (subs.TryGetValue(name, out var subscriptions))
    {
        foreach (var subscription in subscriptions)
        {
            subscription.Invoke(sender, data);
        }
    }
}

然后是 InnerSubscribe,将订阅者添加到订阅集合里

private static void InnerSubscribe(object subscriber, string name, MethodInfo methodInfo, object target)
{
    if (subscriber == null)
        throw new NullReferenceException();
    if (string.IsNullOrEmpty(name))
        throw new NullReferenceException();
    var sub = new Subscription(subscriber, methodInfo, target);
    if (subs.ContainsKey(name))
    {
        subs[name].Add(sub);
        return;
    }
    subs.Add(name, new List<Subscription>() { sub });
}

接下来实现订阅类,他将订阅者规范成统一的类型,方便消息传递

class Subscription
{
    public Subscription(object source, MethodInfo methodInfo, object target)
    {
        Source = source;
        MethodInfo = methodInfo;
        Target = target;
    }
    public object Source { get; set; }
    public MethodInfo MethodInfo { get; set; }
    public object Target { get; set; }
    
    //执行订阅响应
    public void Invoke(object sender, object data)
    {
        if (sender == null || sender == Source) return;
        if (MethodInfo.IsStatic)
        {
            MethodInfo.Invoke(null, MethodInfo.GetParameters().Length == 1 ? new[] { sender } : new[] { sender, data });
            return;
        }
        MethodInfo?.Invoke(Target, MethodInfo.GetParameters().Length == 2 ? new[] { sender, data } : new[] { sender });
    }
}

最后,别忘了使用字典把订阅者存下来

public static class MessageCenter
{
    private static Dictionary<string, List<Subscription>> subs = new Dictionary<string, List<Subscription>>();
    
    //.....内部代码
    
}

使用也是非常的方便,Bar 向消息中心订阅通道,Foo 向消息中心推送消息,完成后会打印出从 Foo 传到 Bar 的消息

public class Bar
{
    public void Start()
    {
        MessageCenter.SubScribe<Foo,string>(this, "测试通道", Handle);
    }
    
    private void Handle(Foo sender,string data)
    {
        Console.WriteLine(data);
    }
}

public class Foo
{
    public void Start()
    {
        MessageCenter.Send(this, "测试通道", "这是一条测试信息");
    }
}
Foo foo = new Foo();
Bar bar = new Bar();
//注意,这里要先注册订阅
bar.Start();
foo.Start();
发布时间:2021-11-26
其他阅读

LinqPad 8 分享下载

LinqPad是一个 .Net 快速验证工具,使得.Net开发者可以不借助 Visual Studio 就可以快速验证想法,毕竟vs冷启动和创建项目耗时久,负担大。本文分享一个LinqPad学习版,可直接下载使用。

查看原文

使用C#接入DeepSeek API实现自己的AI助手

过年期间DeepSeek非常火爆,这段时间用着官方的客户端访问,总是会提示“服务器繁忙,请稍后再试。”,本文介绍怎么通过接入DeepSeek的API实现自己的客户端。

查看原文

命令行打包.net项目

.net 日常开发中,我们接触最多的就是 Visual Studio ,它是微软为了 .net 平台专门打造的 IDE (集成开发环境),为整个 .net 平台开发带来了无与伦比的图形化体验,但是有时候,我们也会遇到需要通过命令行来生成 .net 项目的情况,本文会介绍几种命令行打包的姿势。

查看原文

使用Cherry Studio搭配DeepSeek构建知识库

上篇文章,介绍了怎么在本地部署DeepSeek大模型服务,但是仅限于对话聊天,本文在之前的基础上,用DeepSeek搭配上Cherry Studio来构建个人知识库。

查看原文

WPF使用云母材质

在最新的Windows 11 OS中,微软为流畅设计(Fluent Design)带来了新的云母材质,云母材质一种不透明的动态效果,微软将其作为Windows 11应用窗体的默认材质。

查看原文