今日概念与语法总结
日期:2026-08-04
一、语法点
| 语法 | 一句话 |
|---|---|
public class X : IDisposable |
公开类 + 承诺提供资源释放方法 |
| 构造函数 | 跟类同名、无返回值类型,new 时自动调用 |
I 开头类型 |
接口,只定义方法签名不实现 |
| 依赖注入 | 不自造对象,等外面传现成的 |
IOptionsMonitor<T> |
实时配置监听,文件改了就感知 |
ILoggerFactory |
工厂,能按需造出不同标签的日志记录器 |
ILogger<T> |
泛型参数自动取类名作日志标签 |
CancellationTokenSource |
类类型,既能主动发出取消,也能读令牌 |
CancellationToken |
值类型结构体,只能被动检查状态 |
CreateLinkedTokenSource |
把多个令牌串成 OR 逻辑 |
null! |
null 宽容操作符,赋空值同时抑制编译器警告 |
二、重要概念具体说明
2.1 依赖注入
是什么 :构造函数不自己 new 对象,而是要求外面传一个现成的进来。
csharp
// ❌ 硬编码------把自己和具体实现焊死了
class Worker {
private ITool _tool;
public Worker() {
_tool = new Hammer(); // 只能用锤子,换不了
}
}
// ✅ 依赖注入------外面给什么用什么
class Worker {
private ITool _tool;
public Worker(ITool tool) {
_tool = tool; // 传锤子用锤子,传扳手用扳手
}
}
好处:换实现只改外面注入,Worker 一行不动。
类比:不自己进厨房炒菜,服务员端上来什么吃什么。换厨师不用换盘子。
2.2 接口 vs 实现
是什么:参数类型写接口(壳),实际传进来的是具体类对象(肉)。
csharp
// 接口:只定义方法签名
interface ITool {
void Use();
void Stop();
}
// 实现:具体干活
class Hammer : ITool {
public void Use() { /* 砸 */ }
public void Stop() { /* 停 */ }
}
// Worker 只认接口,不关心底层是 Hammer 还是 Wrench
class Worker {
public Worker(ITool tool) { ... }
}
类比:插座(接口)形状标准化,插什么电器(实现)都行。灯、风扇、充电器------只要插头符合规格。
2.3 IOptionsMonitor(实时配置)
是什么:配置文件在磁盘上改了,立即感知到。
对比:
csharp
// IOptions ------ 死的,启动时拍快照
class Worker {
public Worker(IOptions<Config> cfg) {
var v = cfg.Value.Voltage; // 永远是启动时的值
}
}
// IOptionsMonitor ------ 活的,文件改了就感知
class Worker {
public Worker(IOptionsMonitor<Config> cfg) {
var v = cfg.CurrentValue.Voltage; // 每次读都是最新的
}
}
底层:开了文件监听器。磁盘变动 → 系统通知 → 重新加载。
Microsoft 在配置扩展包里定义了三种:启动读一次不更新、每次请求读一次、实时监听。这是框架写死的。
2.4 工厂模式(ILoggerFactory)
是什么:不传成品,传"能造成品的机器"。
csharp
// ❌ 直接传成品------只能固定用一种日志记录器
class Worker {
public Worker(ILogger logger) {
logger.Log("Worker 的消息"); // 只能打一种标签
}
}
// ✅ 传工厂------内部按需造不同标签的日志记录器
class Worker {
private ILogger _logger;
private ILogger _subLogger;
public Worker(ILoggerFactory factory) {
_logger = factory.CreateLogger("Worker"); // 主标签
_subLogger = factory.CreateLogger("SubModule"); // 子模块标签
}
}
为什么用泛型 CreateLogger<T>:自动取类名当标签,不用手写字符串。
类比:自动售货机------说"可乐"吐可乐,说"雪碧"吐雪碧。你不用知道怎么造的。
2.5 日志五级别
csharp
logger.LogTrace("最细跟踪");
logger.LogDebug("调试信息");
logger.LogInformation("正常记录"); // 日常最常用
logger.LogWarning("警告但还能跑");
logger.LogError("出错了"); // 必须处理
实际输出受配置控制------设定只显示 Warning 以上,Info 及以下就不输出。
2.6 统一接口 + 可换底层
微软定接口,各家实现,业务代码只认接口。
业务代码 → ILogger 接口(统一层)
↓
Serilog / NLog / Console(底层,注入时指定)
换底层:注入时切一行,业务代码不感知。本项目底层是 Serilog------按天切割文件,不同模块独立日志。
2.7 取消令牌三层设计
背景:任务需要响应两种取消来源------外部停止按钮 + 内部异常自主停止。同时要把"谁有权喊停"和"谁只能感受"分开。
csharp
class Worker {
private CancellationTokenSource _selfCts; // 内部喊停源
private CancellationTokenSource _linkedCts; // 合并后的源
public CancellationToken CancelToken; // 只读输出
public Worker(CancellationToken externalToken) {
// 第一层:自己创建一个独立的取消源
_selfCts = new CancellationTokenSource();
// 第二层:把自己 + 外部 串成 OR
_linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
externalToken, // 外部(停止按钮)
_selfCts.Token // 内部(异常自主停止)
);
// 第三层:只输出只读令牌,不给 Cancel 权力
CancelToken = _linkedCts.Token;
}
void OnError() {
_selfCts.Cancel(); // 内部异常时自己喊停
}
void DoWork() {
while (!CancelToken.IsCancellationRequested) {
// 干活...任一源头取消就自动跳出
}
}
}
串联逻辑 :CreateLinkedTokenSource(A, B) = A 或 B 任意取消,合并后的就取消。就像串联电路两个开关------谁拉闸都断电。
为何三层:
| 层 | 能力 |
|---|---|
_selfCts |
内部主动 Cancel |
_linkedCts |
内外 OR,也能 Cancel |
CancelToken |
只读,只能检查 IsCancellationRequested,不能 Cancel |
把 _linkedCts 的 Cancel() 留在手里,只把 .Token(只读副本)放出去------防止其他代码误操作把任务停了。
类比 :遥控器只有你有(_linkedCts),别人只能看屏幕亮了没(CancelToken),不能换台。
2.8 CancellationTokenSource vs CancellationToken
csharp
// CancellationTokenSource ------ 能主动喊停的完整源
var source = new CancellationTokenSource();
source.Cancel(); // ✅ 主动喊停
bool stopped = source.Token.IsCancellationRequested; // ✅ 也能检查状态
// CancellationToken ------ 只能被动检查,没有 Cancel 方法
CancellationToken token = source.Token;
// token.Cancel(); // ❌ 编译错误!没这个方法
bool stopped = token.IsCancellationRequested; // ✅ 只有这一个能力
| CancellationTokenSource | CancellationToken | |
|---|---|---|
| 类型 | 类(引用类型) | 结构体(值类型) |
| 能 Cancel? | ✅ | ❌ |
| 能查状态? | ✅ | ✅ |
| 谁用 | 控制端 | 检测端 |
2.9 null! 操作符
C# 8.0 引入。
csharp
// ❌ 不加 ! ------ 编译器黄线警告:非空类型不该为 null
WorkerInfo info = null;
// ✅ 加 ! ------ 告诉编译器:闭嘴,后面一定赋值
WorkerInfo info = null!;
什么时候用:声明时暂赋空值,后面的 if 分支里才真正赋值。
本质:不是语法需要,是压制编译器警告。
2.10 基础设施 vs 业务数据
csharp
class Worker {
public Worker(
ITool tool, // 基础设施:换任务不变
ILogger log, // 基础设施
CancellationToken tok,// 基础设施
TaskData data // 业务数据:换任务全变
) { ... }
}
| 类别 | 包含什么 | 换任务后 |
|---|---|---|
| 基础设施 | 硬件、日志、配置、取消信号 | 不变 |
| 业务数据 | 任务目标、任务参数 | 全变 |
类比:厨房(基础设施=锅碗灶台)vs 菜单(业务=今天吃什么)。
三、AI 规则(元认知)
每条规则堵同一个洞:"看到输入→自动填空→直接输出"。
核心:所有任务的第一句话只能是确认复述,不准其他任何内容。
- 只说确认,等点头
- 一次一个点
- 讲完停
- 简洁
- 多个概念拆开
- 记录文件为唯一规则来源
- 每段结尾必带问句
- 只想当前,憋回去
- 每行必讲不跳过