zoukankan      html  css  js  c++  java
  • .net core 常见设计模式-IChangeToken

    场景

    一个对象A,希望它的某些状态在发生改变时通知到B(或C、D),
    常见的做法是在A中定义一个事件(或直接用委托),当状态改变时A去触发这个事件。而B直接订阅这个事件
     

    这种设计有点问题
    B由于要订阅A的事件,所以B得完全引用A,其实有时候没必要,因为我只关心A的状态变化而已
    状态变更通知这种场景很多,有没有更通用的方式呢?

    解决思路

    有个谁说的碰到问题加个中间层就解决了,如果解决不了就再加一层

    A和B都引用ChangeToken,
    B向ChangeToken注册一个委托说:将来你有变化时回调我这个委托
    当A的状态变化时会调用ChangeToken的一个方法,这个方法内部就会去触发执行B之前塞进去的委托
    此时比如有组件C、D、E..都关心A的状态变化,也可以引用ChangeToken,并向其注册自己的委托
    这样ChangeToken可以作为一个通用组件,在很多需要更改通知是场景中使用,如:asp.net core的配置系统、终结点路由、 ....

    实现

    微软定义了一个IChangeToken
    HasChanged:表示当前这个ChangeToken是否变化过了
    ActiveChangeCallbacks:当 A触发ChangeToken发生变化时是否主动回调B塞进来的委托
    RegisterChangeCallback(Action<object> callback, object state):提供一个方法,允许调用方塞入委托,B就是调用这个方法向ChangeToken塞入委托的。有一种情况是B希望在塞入委托的同时附带一个状态对象,将来委托被执行时这个状态对象作为执行委托的参数

    CancellationChangeToken是一个用的比较多的实现类,它包含一个CancellationToken属性,这个属性是通过构造函数来初始化的(CancellationTokenSource、CancellationToken自行查询相关资料),
    简化的源码如下:

     1 public class CancellationChangeToken : IChangeToken
     2 {
     3         public CancellationChangeToken(CancellationToken cancellationToken)
     4         {
     5             Token = cancellationToken;
     6         }
     7 
     8         public bool ActiveChangeCallbacks { get; private set; } = true;
     9 
    10         public bool HasChanged => Token.IsCancellationRequested;
    11 
    12         private CancellationToken Token { get; }
    13 
    14         public IDisposable RegisterChangeCallback(Action<object> callback, object state)
    15         {
    16               return Token.Register(callback, state);           
    17         }
    18     }

    因为CancellationToken天然的已经现了IChangeToken,因此CancellationChangeToken只是对CancellationToken的包装。那为啥不直接让CancellationToken实现IChangeToken呢?我感觉是设计意图不同,CancellationToken设计时主要是考虑应用在取消异步操作这件事上的,只是碰巧取消异步操作与更改通知设计思路是相似的,所以才出现CancellationChangeToken

    其它的实现类没去研究过,但是只要你对这种设计思路理解了,碰到其它实现类应该看看就明白了

    例子

    下面我们使用CancellationChangeToken来完成上面的A、B类,A类状态变化时 通知到B类(其实就是执行B类忘ChangeToken中塞入的委托),完整源码如下:

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             Console.OutputEncoding = Encoding.UTF8;
     6             CancellationTokenSource cts = new CancellationTokenSource();
     7             CancellationChangeToken cct = new CancellationChangeToken(cts.Token);
     8             var a = new A(cts);
     9             var b = new B(cct);
    10             Console.ReadKey();
    11         }
    12     }
    13 
    14     public class A
    15     {
    16         CancellationTokenSource _cts;
    17         public A(CancellationTokenSource cts)
    18         {
    19             this._cts = cts;
    20             Task.Run(() =>
    21             {
    22                 Task.Delay(2000).Wait();
    23                 Console.WriteLine("模拟触发更改通知");
    24                 _cts.Cancel();
    25             });
    26         }
    27     }
    28     public class B
    29     {
    30         public B(CancellationChangeToken cct) {
    31             object testState = 1;
    32             cct.RegisterChangeCallback(obj => {
    33                 //将来cct检测到变化时此委托会被执行
    34                 //obj是注册委托是传递进来的参数,就是这里的testState
    35                 Console.WriteLine($"状态变化了,状态值{obj}");
    36             }, testState);
    37         }
    38     }

    上面只是演示IChangeToken的思路,asp.net core中源码的应用时通常是在A中提供一个返回IChangeToken的方法

    无限监控与ChangeToken.OnChange

    上面的方式只能变更通知一次,下面可以永远监控

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             Console.OutputEncoding = Encoding.UTF8;
     6             var a = new A();
     7             //实现无限监控状态变化。OnChange有两个委托类型的参数,我们分别称为委托1和委托2
     8             ChangeToken.OnChange(() => a.CreateChangeToken(), () => Console.WriteLine("a状态变化了"));
     9             Console.ReadKey();
    10         }
    11     }
    12 
    13     public class A
    14     {
    15         CancellationTokenSource _cts;
    16         public A()
    17         {
    18             Task.Run(() =>
    19             {
    20                 while (true)
    21                 {
    22                     Task.Delay(2000).Wait();
    23                     Console.WriteLine("模拟两秒一次触发一次状态修改通知");
    24                     this._cts.Cancel();
    25                 }
    26             });
    27         }
    28 
    29         public IChangeToken CreateChangeToken() {
    30             _cts = new CancellationTokenSource();
    31             return new CancellationChangeToken(_cts.Token);
    32         }
    33     }

    重点是这句:ChangeToken.OnChange(() => a.CreateChangeToken(), () => Console.WriteLine("a状态变化了"));
    OnChange有两个委托类型的参数,我们分别称为委托1和委托2,当a的状态变化后会执行委托2,之后会执行委托1,当a状态又变化时又会执行委托2,之后执行委托1,如此往复实现无限监控 

    这是今天学习的内容,可能理解得不是很准确,仅供参考...

  • 相关阅读:
    c#时间函数
    .NET中的lock(C#版本)
    关于OpenSmtp邮件标题过长,附件名,用户名出现乱码问题的终及解决Dll文件
    Windbg 用法
    使用OpenXML将数据导入到Excel模板中
    Compiere源码workspace的两个配置文件内容
    我学MEF系列(8):MEF+Unity实现可扩展拦截器
    基于插件架构的简单的Winform框架(上)
    我学Flash/Flex(2):AS3读取XML文件内容
    我学Flash/Flex(1):Action Script3.0基础知识
  • 原文地址:https://www.cnblogs.com/jionsoft/p/12249326.html
Copyright © 2011-2022 走看看