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
其他阅读

WPF中创建一个矩形圆角动画

WPF 中内置了好几种动画,大多数场景可以坐到开箱即用,不过并没有内置 CornerRadiusAnimation ,本文将会介绍怎么实现一个 CornerRadiusAnimation 动画,实现 BorderCornerRadius 属性动画效果。

查看原文

Angular使用路由复用实现单页多窗(Tab)

我们在开发后台管理系统时,一个很重要的需求就是多窗口编辑,来回切换使用。一种思路是使用iframe来记录多个多页面展示,本文通过Angular路由复用来实现这一需求。

查看原文

Fetch发送请求

fetch 是 javascript 中一个新的 api,用于访问和控制 HTTP 的请求和响应等,不再需要使用传统的 XMLHttoRequest

查看原文

Web前端中实现自定义右键菜单

在原生的桌面应用中,右键菜单是个很常见也很常用的东西,但是在Web应用中,由于浏览器自带了右键菜单,所以我们很少见到应用有自己的右键菜单,但不常见并不代表没有,本文就会介绍一个右键菜单的实现。

查看原文

JavaScript构建一个Markdown解析器

Markdown是一种轻量级标记语言,以简单易读的语法和可生成丰富多彩的内容,受到开发者们的青睐。无论是文档编写,还是博客传记都广泛使用到Markdown。本文介绍试用JavaScript来构建一个Markdown解析器,解锁Markdown的强大之处。

查看原文