看看这个接口长什么样子
namespace Microsoft.Extensions.Primitives
{
public interface IChangeToken
{
bool HasChanged { get; }
bool ActiveChangeCallbacks { get; }
IDisposable RegisterChangeCallback(Action<object> callback, object state);
}
}
IChangeToken从字面理解就是某种跟变化相关的令牌。其实它可以在检测到数据发生变化时发送一个通知,如果数据发生变化,则HasChanged属性就会变成true。ActiveChangeCallbacks这个属性用于表示当数据发生变化时,是否主动执行注册的回调函数,通常这个值设置为true。如果设置为false,则不会调用回调。
RegisterChangeCallback注册一个当数据发生变化时,自动执行的回调,它返回值为IDisposable对象,可以使用其Dispose方法解除掉注册的回调函数。
也许大家会好奇这个IChangeToken似乎很少见到,其实不然,IChangeToken在.net core的世界中可谓是无处不在,只不过以xxxChangeToken这种命名的方式存在,换句话说,当你看到一个类名为xxxChangeToken,那么大概率是实现了IChangeToken这个接口。
比如在文件系统中的PollingFileChangeToken,配置系统中的ConfigurationReloadToken,如下图
下面我使用监控文件变化,来演示一下IChangeToken的运行过程。
using (var fileProvider=new PhysicalFileProvider("d:\\test"))
{
IChangeToken changeToken = fileProvider.Watch("test.txt");
changeToken.RegisterChangeCallback(_ =>
{
Logger.ToConsole("file changed!",ConsoleColor.DarkGreen);
},"");
for (int i = 0; i < 3; i++)
{
Logger.ToConsole("add text to file", ConsoleColor.DarkGray);
await File.AppendAllTextAsync(@"d:\test\test.txt", DateTime.Now.ToString());
await Task.Delay(2000);
}
}
PhysicalFileProvider类中的Watch方法在文件变化时会返回一个IChangeToken,然后注册一个回调函数放入RegisterChangeCallback中。这时我们每隔一段时间修改一下文本,模拟文件修改操作,效果如下。
可以看到,我们修改了三次文件,但是回调函数只执行了一次,说明这个“令牌”具有“一次性”的特征,也就是说,使用了一次,也就失效了。
如果想每次更改就触发回调怎么办呢,这就引出今儿的另一个主角,ChangeToken。
别看ChangeToken和IChangeToken就差了一个“I”,但其实ChangeToken并非实现了IChangeToken接口。ChangeToken是一个静态类,对外仅暴露OnChange函数。我们来看看这个函数长什么模样
OnChange函数接收一个返回值为IChangeToken的“producer”以及一个当令牌发生改变时,自从执行的回调函数“consumer”。我们按照这个对上面程序改造一下
ChangeToken.OnChange(
() => fileProvider.Watch("test.txt"),
() => Logger.ToConsole("file changed!", ConsoleColor.DarkGreen)
);
我们看到每次文件发生变化,都会自动执行塞进去的回调函数。
ChangeToken的OnChange函数在.net core中还是蛮常见的,最典型的应用恐怕就是大家熟知的配置文件更改时,自动加载配置项吧,大家如果感兴趣可以在源代码中查看FileConfigurationProvider一窥端倪。
这里再额外啰嗦两句,也许您会好奇OnChange函数是如何实现这种无限监控变化并执行的呢?
其实原理蛮简单的,简单来说,它利用您传入的producer获取IChangeToken,并注册IChangeToken的RegisterChangeCallback函数,在这个函数内部,先获取新的token,然后执行传入的consumer函数,最后利用新的token再来一遍,以此反复。我画了一幅草图帮助大家理解,仅仅是抛砖引玉,感兴趣的小伙伴可以再深入研究。
聊到这里IChangeToken和ChangeToken已经算是接近尾声了,也许您会好奇如何自己实现一个xxChangeToken,其实很简单,只要借助CancellationTokenSource这个类,您也可以轻松实现,事实上在.net core中大多数的xxChangeToken都是借助它来实现的。如下
public class ConfigurationReloadToken : IChangeToken
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
……
}
我在示例代码中,自定义了一个IChangeToken的实现,并展示数据发生变化时,如何响应,算是简单模拟了一下.net core配置文件变化时,是如何重新加载配置数据的,鉴于时间和篇幅,我就不在此展开了,感兴趣的小伙伴可以去一探究竟。
在最后,也许对于这种状态改变执行回调这样的操作,大家更熟悉的是使用事件订阅的方式,假如B关心A的变化,那么当A发生变化时,可以触发事件,B去订阅事件。这么做是完全可行的,但存在的最大的问题就是B需要紧密的耦合A,也就是B需要引用A。但往往作为B来说,仅仅是关心A的变化而已。这时候使用ChangeToken其实就是当做一个靠谱的中间层,A只要告诉它它发生了改变,而B只需要告诉它A变化时,我要做什么就ok了。
本文转自:https://zhuanlan.zhihu.com/p/344370027
本文链接:https://blog.nnwk.net/article/1543
有问题请留言。版权所有,转载请在显眼位置处保留文章出处,并留下原文连接
Leave your question and I'll get back to you as soon as I see it. All rights reserved. Please keep the source and links
友情链接:
子卿全栈
全部评论