前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#设计模式之订阅发布模式

C#设计模式之订阅发布模式

作者头像
HueiFeng
发布2020-05-25 17:12:55
9520
发布2020-05-25 17:12:55
举报

什么是Pub-Sub

发布订阅是一种设计模式,它允许应用程序组件之间进行松散耦合。 其实订阅发布设计中主要是发布者生成事件通道,用于在不了解任何订阅者存在的情况下通知订阅者。

当然委托EventHandlers和Event关键字在此事件处理机制中担任着重要的角色。下面我们来看看如何使用它们。

Pub和Sub的使用

首先我们看一个简单地订阅发布模式.

定义一个Action委托,无返回值.

namespace PubSubPattern
{
    public class Pub
    {
        public Action OnChange { get; set; }

        public void Raise()
        {
            if (OnChange != null)
            {
                //Invoke OnChange Action
                OnChange();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var p = new Pub();
            p.OnChange += () => Console.WriteLine("Sub 1");

            p.OnChange += () => Console.WriteLine("Sub 2");

            p.Raise();

            Console.WriteLine("Press enter !");
            Console.ReadLine();

        }
    }
}

如上代码我们创建了一个发布者,并且我们调用委托进行创建我们匿名方法来订阅。由于委托提供了多播功能,因此我们可以OnChange属性上使用+=.

虽然说我们看着如上代码执行无误,但是程序中仍然存在一些问题,如果使用=而不是+=,那么OnChange属性中将会删除第一个订阅者。 由于OnChange是公共属性,因此该类的任何外部用户都可以进行调用p.OnChange().

使用Event关键字的发布订阅

下面我们来看看使用event关键字后的代码

    public class Pub
    {
        public event Action OnChange = delegate { };

        public void Raise()
        {
            OnChange();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Pub p = new Pub();
            p.OnChange += () => Console.WriteLine("Sub 1");
            p.OnChange += () => Console.WriteLine("Sub 2");
            p.Raise();
            Console.WriteLine("Press enter !");
            Console.ReadLine();
        }
    }

通过如上代码我们试着去解决我们第一处所说的问题,我们会发现使用event关键字后可以保护我们OnChange免受不必要的访问。它不允许使用=也就是说他不允许直接进行分配委托,因此我们现在可以避免使用=,从而避免应用程序不必要的麻烦。

可能大家也会发现OnChange初始化为空委托delegate{}。这样可以确保我们的OnChange永远不会为空。因为当我们其他进行对他调用的时候我们可以在代码中进行删除对他的非空检查.

使用EventHandlers的发布订阅

其实在订阅发布中,发布者和订阅者都不知道彼此的存在。有个EventHandler,它被称为消息代理或者说事件总线,发布者和订阅者都应该知道它,它接收所有传入的消息并且将它们进行转发.

因此呢,在如下片段中我们使用EventHandler而不是用Action.

public delegate void EventHandler(
    object sender,
    EventArgs e
)

默认情况下,EventHandler将发送对象和一些事件参数作为参数。

  public class MyEventArgs : EventArgs
        {
            public int Value { get; set; }

            public MyEventArgs(int value)
            {
                Value = value;
            }
        }

        public class Pub
        {
            public event EventHandler<MyEventArgs> OnChange = delegate { };
            public void Raise()
            {
                OnChange(this, new MyEventArgs(1));
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Pub p = new Pub();
                p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
                p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
                p.Raise();
                Console.WriteLine("Press enter !");
                Console.ReadLine();
            }
        }

如上代码中通过pub类使用通用的EventHandler,它触发EventHandler OnChange时需要传递的事件参数类型,在上面代码片段中为MyArgs

事件中的异常

我们继续说一种情况.大家看如下代码片段

    public class MyEventArgs : EventArgs
    {
        public int Value { get; set; }

        public MyEventArgs(int value)
        {
            Value = value;
        }
    }

    public class Pub
    {
        public event EventHandler<MyEventArgs> OnChange = delegate { };
        public void Raise()
        {
            OnChange(this, new MyEventArgs(1));
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Pub p = new Pub();
            p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
            p.OnChange += (sender, e) => { throw new Exception(); };
            p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
            p.Raise();
            Console.WriteLine("Press enter !");
            Console.ReadLine();
        }
    }

运行如上代码后,大家会发现第一个订阅者已经执行成功了,第二个订阅者引发了异常,而第三个订阅者未被调用.这是一个很尴尬的事情.

如果说我们觉得如上的过程不是我们预期的,我们需要手动引发事件并处理异常,这时候我们可以使用Delegate基类中定义的GetInvoctionList来帮助我们实现这些。

我们继续看如下代码

public class MyEventArgs : EventArgs
        {
            public int Value { get; set; }

            public MyEventArgs(int value)
            {
                Value = value;
            }
        }

        public class Pub
        {

            public event EventHandler<MyEventArgs> OnChange = delegate { };

            public void Raise()
            {
                MyEventArgs eventArgs = new MyEventArgs(1);

                List<Exception> exceptions = new List<Exception>();

                foreach (Delegate handler in OnChange.GetInvocationList())
                {
                    try
                    {
                        handler.DynamicInvoke(this, eventArgs);
                    }
                    catch (Exception e)
                    {
                        exceptions.Add(e);
                    }
                }

                if (exceptions.Any())
                {
                    throw new AggregateException(exceptions);
                }
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Pub p = new Pub();
                p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
                p.OnChange += (sender, e) => { throw new Exception(); };
                p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
                p.Raise();
                Console.WriteLine("Press enter !");
                Console.ReadLine();
            }
        }

Reference

https://github.com/hueifeng/DesignPatterns-Samples/tree/master/PubSubPattern

https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是Pub-Sub
  • Pub和Sub的使用
  • 使用Event关键字的发布订阅
  • 使用EventHandlers的发布订阅
  • 事件中的异常
  • Reference
相关产品与服务
事件总线
腾讯云事件总线(EventBridge)是一款安全,稳定,高效的云上事件连接器,作为流数据和事件的自动收集、处理、分发管道,通过可视化的配置,实现事件源(例如:Kafka,审计,数据库等)和目标对象(例如:CLS,SCF等)的快速连接,当前 EventBridge 已接入 100+ 云上服务,助力分布式事件驱动架构的快速构建。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档