【.NET】聊聊 IChangeToken 接口

发布时间:2026/7/26 8:56:56

【.NET】聊聊 IChangeToken 接口 【.NET】聊聊 IChangeToken 接口在 .NET 开发中我们经常需要处理“当某个东西发生变化时通知相关代码”的场景。比如配置文件改了应用能自动重载或者缓存过期了需要刷新。传统的做法是写事件、回调或者手动轮询但这些方式要么耦合度高要么效率低。而 .NET 提供了一个优雅的解决方案——IChangeToken接口。它就像是一个“变化通知器”让代码可以在不主动询问的情况下被动感知变化。今天我们就来聊聊这个接口是什么、怎么用以及它背后的设计思想。## 什么是 IChangeTokenIChangeToken是 .NET 中位于Microsoft.Extensions.Primitives命名空间下的一个接口。它定义了一个“变化令牌”用于跟踪某个资源是否发生了变化。想象一下你有一个“文件监视器”当文件内容改动时它会触发一个信号。IChangeToken就是这个信号的抽象它告诉你“是否改变了”并且可以让你注册一个回调在改变时执行。接口定义非常简洁csharppublic interface IChangeToken{ bool HasChanged { get; } bool ActiveChangeCallbacks { get; } IDisposable RegisterChangeCallback(Actionobject? callback, object? state);}-HasChanged一个布尔属性表示令牌是否已经触发过变化即资源是否已经改变。-ActiveChangeCallbacks表示令牌是否支持主动回调即是否可以在变化时调用注册的回调。如果为false你可能需要手动检查HasChanged。-RegisterChangeCallback注册一个回调方法当变化发生时会被调用。它返回一个IDisposable用于取消注册。这个接口的核心思想是“推模式”资源变化时主动通知依赖方而不是让依赖方轮询。这类似于观察者模式。## 为什么需要 IChangeToken在 .NET 中IChangeToken主要用在配置系统、缓存系统和文件监视中。例如1.配置文件热重载当appsettings.json发生变化时自动更新IConfiguration实例。2.缓存失效基于文件变化的缓存过期机制如MemoryCache中的ChangeToken。3.自定义变化通知你可以实现自己的令牌用于数据库变更、API 响应等场景。使用IChangeToken可以解耦变化检测和响应逻辑让代码更模块化、更易测试。## 代码示例使用内置的 IChangeToken.NET 提供了一些内置的IChangeToken实现比如基于文件变化的PollingFileChangeToken和FileSystemWatcher的包装。但更常见的是使用ChangeToken静态类和CancellationChangeToken。下面是一个使用CancellationChangeToken的示例它基于CancellationToken来模拟变化通知csharpusing System;using System.Threading;using System.Threading.Tasks;using Microsoft.Extensions.Primitives;public class CancellationChangeTokenExample{ public static async Task RunAsync() { // 创建一个 CancellationTokenSource用于模拟变化信号 using var cts new CancellationTokenSource(); // 创建 IChangeToken 实例 IChangeToken token new CancellationChangeToken(cts.Token); Console.WriteLine(开始监视变化...); // 注册回调当变化发生时执行 using (token.RegisterChangeCallback(state { Console.WriteLine($变化被检测到状态: {state}); }, Hello from callback)) { // 模拟延迟后触发变化 await Task.Delay(1000); Console.WriteLine(触发变化...); cts.Cancel(); // 取消令牌触发回调 // 检查 HasChanged 属性 Console.WriteLine($HasChanged 属性: {token.HasChanged}); } Console.WriteLine(监视结束。); }}// 在 Main 中调用// await CancellationChangeTokenExample.RunAsync();输出开始监视变化...触发变化...变化被检测到状态: Hello from callbackHasChanged 属性: True监视结束。在这个例子中CancellationChangeToken在CancellationToken被取消时触发回调。注意CancellationChangeToken是一次性的——一旦取消HasChanged就永远为true且回调只执行一次。这适合“一次性变化”的场景。## 代码示例自定义 IChangeToken 实现如果内置的令牌不满足需求你可以自己实现IChangeToken。比如实现一个基于时间间隔的轮询令牌定期检查变化。但更常见的场景是结合FileSystemWatcher实现文件变化通知。下面是一个自定义令牌它基于一个简单的计数器每次计数增加时触发变化csharpusing System;using System.Threading;using Microsoft.Extensions.Primitives;public class CounterChangeToken : IChangeToken{ private int _lastCount; // 上一次检查的计数值 private readonly Funcint _getCurrentCount; // 获取当前计数的函数 private Actionobject?? _callback; // 注册的回调 private object? _callbackState; public CounterChangeToken(Funcint getCurrentCount) { _getCurrentCount getCurrentCount; _lastCount getCurrentCount(); } // 当计数增加时HasChanged 为 true public bool HasChanged { get { int current _getCurrentCount(); bool changed current ! _lastCount; if (changed) { _lastCount current; // 触发回调 _callback?.Invoke(_callbackState); } return changed; } } // 支持主动回调当 HasChanged 为 true 时会调用回调 public bool ActiveChangeCallbacks true; // 注册回调 public IDisposable RegisterChangeCallback(Actionobject? callback, object? state) { _callback callback; _callbackState state; // 返回一个简单的 IDisposable用于取消注册 return new Unsubscriber(() _callback null); } // 内部类用于取消注册 private class Unsubscriber : IDisposable { private readonly Action _unsubscribe; public Unsubscriber(Action unsubscribe) _unsubscribe unsubscribe; public void Dispose() _unsubscribe(); }}public class CustomChangeTokenExample{ public static void Run() { int counter 0; var token new CounterChangeToken(() counter); Console.WriteLine(开始监视计数变化...); // 注册回调 using (token.RegisterChangeCallback(state { Console.WriteLine($计数已变化当前计数: {state}); }, counter)) { // 模拟变化 counter 1; Console.WriteLine($检查 HasChanged: {token.HasChanged}); // 应触发回调 counter 2; Console.WriteLine($再次检查 HasChanged: {token.HasChanged}); // 再次触发 counter 2; // 没有变化 Console.WriteLine($检查 HasChanged (无变化): {token.HasChanged}); // false } Console.WriteLine(监视结束。); }}// 在 Main 中调用// CustomChangeTokenExample.Run();输出开始监视计数变化...计数已变化当前计数: 0检查 HasChanged: True计数已变化当前计数: 1再次检查 HasChanged: True检查 HasChanged (无变化): False监视结束。这个自定义令牌展示了如何实现IChangeToken的完整逻辑通过检查HasChanged来触发回调。注意RegisterChangeCallback通常只注册一个回调但实际框架中可能支持多个如CompositeChangeToken可以组合多个令牌。这里为了简单只支持一个。## IChangeToken 在 .NET 中的实际应用### 配置系统热重载在 ASP.NET Core 中配置文件变化时IConfiguration会自动重载。这背后就是IChangeToken在起作用。例如JsonConfigurationProvider内部使用FileWatcherChangeToken来监视文件变化csharp// 伪代码示意var token new FileWatcherChangeToken(appsettings.json);ChangeToken.OnChange(() token, () ReloadConfiguration());当文件改变时token.HasChanged为trueChangeToken.OnChange会自动重新运行ReloadConfiguration。### 缓存过期MemoryCache支持基于IChangeToken的过期策略。你可以创建一个令牌当令牌变化时缓存条目自动失效csharpvar cache new MemoryCache(new MemoryCacheOptions());var cts new CancellationTokenSource();var token new CancellationChangeToken(cts.Token);cache.Set(key, value, new MemoryCacheEntryOptions().AddExpirationToken(token));// 当 cts.Cancel() 时缓存条目被移除这样你可以灵活地控制缓存的生命周期而不需要手动管理。## 总结IChangeToken是 .NET 中一个轻量但强大的变化通知机制。它通过“推模式”解耦了变化检测和响应逻辑让代码更清晰、更可维护。本文介绍了-接口定义HasChanged、ActiveChangeCallbacks和RegisterChangeCallback。-使用示例内置的CancellationChangeToken和自定义CounterChangeToken。-实际应用配置热重载和缓存过期。理解IChangeToken能帮助你更好地利用 .NET 的框架特性比如ChangeToken.OnChange和IChangeToken的组合使用。下次当你需要处理“变化”时不妨考虑用它来替代手动事件或轮询让你的代码更优雅。

相关新闻