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

其他阅读

静态文件防盗链的一种思路

在我们的系统中,总会有一些资源需要保护起来不被盗走,如果是动态接口可以通过验证权限来保护,静态资源大多需要对外开放,比较难以保护,本文就介绍保护静态文件资源一种思路——那就是使用 HTTP 中 Referer 头。

浅析web前端中的MVC模式

MVC是常见的软件架构设计模式,它通过分离关注点改进代码的组织方式。区别于软件设计模式,只是为了解决问题总结出的抽象方法,一种架构模式种往往会用到多种设计模式。

使用中间件记录网站访问日志记录

对于网站访问日志的记录,一般情况下都是使用现有的日志服务,比如谷歌分析引入轻量 js 文件即可。本文主要介绍对于现有的 Asp.Net Core 网站使用中间件快速记录访问日志。使用中间件的好处:在服务端进行处理,无视客户端类型(特殊信息无法获取);统一处理,不需要对每个地址重复处理等。

Blazor文件上传解决方案

Blazor 是由 Asp.Net Core 团队推出的一个Web前端SPA解决方案,其中包括了使用 WebAssembly 的 Blazor Wasm 和使用 SignalR 进行实时交互的 Blazor server。本篇文章中使用的是 Blazor Wasm 方案来验证上传文件的操作。

LinqPad 8 分享下载

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