场景

一个对象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自行查询相关资料),
简化的源码如下:

 public class CancellationChangeToken : IChangeToken
{
public CancellationChangeToken(CancellationToken cancellationToken)
{
Token = cancellationToken;
} public bool ActiveChangeCallbacks { get; private set; } = true; public bool HasChanged => Token.IsCancellationRequested; private CancellationToken Token { get; } public IDisposable RegisterChangeCallback(Action<object> callback, object state)
{
return Token.Register(callback, state);
}
}

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

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

例子

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

     class Program
{
static void Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
CancellationTokenSource cts = new CancellationTokenSource();
CancellationChangeToken cct = new CancellationChangeToken(cts.Token);
var a = new A(cts);
var b = new B(cct);
Console.ReadKey();
}
} public class A
{
CancellationTokenSource _cts;
public A(CancellationTokenSource cts)
{
this._cts = cts;
Task.Run(() =>
{
Task.Delay().Wait();
Console.WriteLine("模拟触发更改通知");
_cts.Cancel();
});
}
}
public class B
{
public B(CancellationChangeToken cct) {
object testState = ;
cct.RegisterChangeCallback(obj => {
//将来cct检测到变化时此委托会被执行
//obj是注册委托是传递进来的参数,就是这里的testState
Console.WriteLine($"状态变化了,状态值{obj}");
}, testState);
}
}

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

无限监控与ChangeToken.OnChange

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

     class Program
{
static void Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
var a = new A();
//实现无限监控状态变化。OnChange有两个委托类型的参数,我们分别称为委托1和委托2
ChangeToken.OnChange(() => a.CreateChangeToken(), () => Console.WriteLine("a状态变化了"));
Console.ReadKey();
}
} public class A
{
CancellationTokenSource _cts;
public A()
{
Task.Run(() =>
{
while (true)
{
Task.Delay().Wait();
Console.WriteLine("模拟两秒一次触发一次状态修改通知");
this._cts.Cancel();
}
});
} public IChangeToken CreateChangeToken() {
_cts = new CancellationTokenSource();
return new CancellationChangeToken(_cts.Token);
}
}

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

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

.net core 常见设计模式-IChangeToken的更多相关文章

  1. JavaScript 中常见设计模式整理

    开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式.本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知. JavaScript 中常见设计模 ...

  2. JS中常见设计模式总结

    github: https://github.com/14glwu/FEInterviewBox/tree/master/JS%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F ...

  3. java之 ------ DAO设计模式的【具体解释】及常见设计模式的【应用】

    DAO Data Access Object(数据訪问接口) 一.场景和问题 在Java程序中.常常须要把数据持久化,也须要获取持久化的数据.可是在进行数据持久化的过程中面临诸多问题(如:数据源 不同 ...

  4. ASP.NET Core MVC 设计模式 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core MVC 设计模式 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core MVC 设计模式 上一章节中,我们提到 ASP.NET Co ...

  5. JAVA设计模式简介及六种常见设计模式详解

    一.什么是设计模式                                                                                           ...

  6. Golang 常见设计模式之单例模式

    之前我们已经看过了 Golang 常见设计模式中的装饰和选项模式,今天要看的是 Golang 设计模式里最简单的单例模式.单例模式的作用是确保无论对象被实例化多少次,全局都只有一个实例存在.根据这一特 ...

  7. 【Java基础】浅谈常见设计模式

    Num1:单例模式 基本概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 常见写法: 懒汉式 public class Singleton { /* 持有私有静态实例,防止被引用,此处赋值 ...

  8. Javascript常见设计模式解析

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.毫无疑问,设计模式于己 ...

  9. Golang 常见设计模式之选项模式

    熟悉 Python 开发的同学都知道,Python 有默认参数的存在,使得我们在实例化一个对象的时候,可以根据需要来选择性的覆盖某些默认参数,以此来决定如何实例化对象.当一个对象有多个默认参数时,这个 ...

随机推荐

  1. GapMinder气泡图:在线互动图表数据平台

    GapMinder:在线互动图表数据平台是一个将国际统计数据转换成活动的.交互的和有趣的图表,以在线统计数据为基础的互动图表集的完美世界.目的是通过增进对可以自由访问的公共统计数据的使用和理解,以促进 ...

  2. Visual Studio Team Services使用教程【6】:Readers tfs组checkin权限修改

    你也可以只开启部分代码的权限 把上面开启的整个应用的权限先去掉 只开启一个文件的权限 2017.4.23之后建议朋友看下面的帖子 TFS2017 & VSTS 实战(繁体中文视频) Visua ...

  3. Qt4.5 QFrame(相当于Delphi里的TPanel,有各种凹凸方式)

    QFrame类是有框架的窗口部件的基类. QPopupMenu使用这个来把菜单“升高”,高于周围屏幕.QProgressBar有“凹陷”的外观.QLabel有平坦的外观.这些有框架的窗口部件可以被改变 ...

  4. docker安装启动、配置MySql

    1.安装mysql镜像 docker pull mysql/mysql-server 2.docker中启动Mysql容器 docker run --name mysql01 -d -p 3306:3 ...

  5. EnvironmentAware接口的作用

    在SpringBoot中的应用 凡注册到Spring容器内的bean,实现了EnvironmentAware接口重写setEnvironment方法后,在工程启动时可以获得application.pr ...

  6. 013 CephFS文件系统

    一.Ceph文件系统简介 CephFS提供兼容POSIX的文件系统,将其数据和与那数据作为对象那个存储在Ceph中 CephFS依靠MDS节点来协调RADOS集群的访问 元数据服务器 MDS管理元数据 ...

  7. C# event 事件-2

    本次是对第一篇事件随笔的补充笔记,涉及题目依然使用上一篇的习题.上一篇地址:https://www.cnblogs.com/FavoriteMango/p/11685702.html 1.事件的定义 ...

  8. [技术翻译]使用Nuxt生成静态网站

    本周再来翻译一些技术文章,本次预计翻译三篇文章如下: 04.[译]使用Nuxt生成静态网站(Generate Static Websites with Nuxt) 05.[译]Web网页内容是如何影响 ...

  9. 2019牛客暑期多校第一场题解ABCEFHJ

    A.Equivalent Prefixes 传送门 题意:给你两个数组,求从第一个元素开始到第p个元素 满足任意区间值最小的元素下标相同的 p的最大值. 题解:我们可以从左往右记录到i为止每个区间的最 ...

  10. 小小TODO标识,你用对了吗?

    前言 有时,您需要标记部分代码以供将来参考,比如: 优化,改进,可能的更改,要讨论的问题等. 通常我们会在代码中加入如下的标记表示待办: //TODO 我将要在这里做 xxx 你这样做,别人也会这样做 ...