服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 深入了解C#设计模式之订阅发布模式

深入了解C#设计模式之订阅发布模式

2022-09-19 17:35HueiFeng C#

这篇文章主要介绍了C#设计模式之订阅发布模式的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下

什么是Pub-Sub

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

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

Pub和Sub的使用

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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关键字后的代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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.

?
1
2
3
4
public delegate void EventHandler(
  object sender,
  EventArgs e
)

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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

事件中的异常

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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来帮助我们实现这些。

我们继续看如下代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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

以上就是深入了解C#设计模式之订阅发布模式的详细内容,更多关于c# 订阅发布模式的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/yyfh/p/12933597.html

延伸 · 阅读

精彩推荐
  • C#C#利用SFTP实现上传下载

    C#利用SFTP实现上传下载

    这篇文章主要为大家详细介绍了C#利用SFTP实现上传下载的相关代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    白铁11422022-01-25
  • C#C#用递归算法解决八皇后问题

    C#用递归算法解决八皇后问题

    在软件编程中,这种思路确是一种解决问题最简单的算法,它通过一种类似于蛮干的思路,一步一步地往前走,每走一步都更靠近目标结果一些,直到遇到...

    张玉彬11122021-11-25
  • C#C#处理猜拳问题的简单实例(非窗体)

    C#处理猜拳问题的简单实例(非窗体)

    下面小编就为大家带来一篇C#处理猜拳问题的简单实例(非窗体)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网9672021-12-01
  • C#Quartz.Net调度框架配置解析

    Quartz.Net调度框架配置解析

    这篇文章主要为大家详细介绍了Quartz.Net调度框架的配置方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    小城岁月10812022-01-17
  • C#SQL+C#实现获得当前月的第一天与最后一天

    SQL+C#实现获得当前月的第一天与最后一天

    本文分享了SQL+C#获得当前月的第一天与最后一天的代码实例,代码简洁,适合初学者参考。需要的朋友可以看下...

    唔愛吃蘋果6232021-12-13
  • C#C#中通过使用Connection类来实现打开/关闭数据库的代码实例

    C#中通过使用Connection类来实现打开/关闭数据库的代码实例

    今天小编就为大家分享一篇关于C#中通过使用Connection类来实现打开/关闭数据库的代码实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值...

    Czhenya3522022-03-03
  • C#C#设计模式之行为型模式详解

    C#设计模式之行为型模式详解

    这篇文章主要为大家详细介绍了C#设计模式之行为型模式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    季末的寂寞11212022-01-17
  • C#C#实现简单的3DES加密解密功能示例

    C#实现简单的3DES加密解密功能示例

    这篇文章主要介绍了C#实现简单的3DES加密解密功能,结合实例形式分析了C#实现3DES加密解密的定义、使用等具体步骤与相关操作技巧,需要的朋友可以参考下...

    5t4rk4752022-01-20