c# 12 的 interceptors 是编译期重写机制,非运行时拦截;要求拦截器与被拦截方法同项目、同编译单元,且签名严格匹配,仅支持 internal/private 方法,需启用预览功能。

Interceptors 是 C# 12 的预览功能,不是运行时拦截器
它不是类似 IAuthorizationFilter 或 IDispatchProxy 那种在程序运行时动态介入调用的机制。C# 12 的 Interceptors 是编译期重写技术:编译器识别标记了 [InterceptsLocation] 的方法,在生成 IL 时,把对目标方法的调用直接替换成对拦截器方法的调用——原方法甚至不会出现在最终输出的程序集中。
这意味着:
- 它不依赖反射、代理或运行时织入,无性能开销
- 仅适用于
internal或private方法(public 方法调用可能跨程序集,无法安全重写) - 必须启用预览功能:
<langversion>12.0</langversion>+<enablepreviewfeatures>true</enablepreviewfeatures> - 目前仅支持源代码在同一项目中,且拦截器和被拦截方法都必须在同一个编译单元内
怎么写一个合法的拦截器方法
拦截器不是任意方法,必须严格满足签名和属性约束,否则编译失败(错误如 CS8954 或 CS8955):
- 返回类型必须与被拦截方法完全一致(含
void、Task、泛型等) - 参数列表必须与被拦截方法**逐个位置、逐个类型匹配**(不能多参、少参、类型隐式转换也不行)
- 必须标注
[InterceptsLocation(...)],参数是字符串字面量,格式为"{文件路径}:{行号}:{列号}",指向被拦截方法的声明位置 - 必须是
static、internal(不能是public或private)
示例:
[InterceptsLocation("Program.cs:12:5")]
internal static string GetGreeting() => "Hello from interceptor!";对应被拦截的方法需写在 Program.cs 第 12 行第 5 列(通常是方法名起始位置):
internal static string GetGreeting() => "Hello world"; // 这行会被完全跳过
常见失败场景:为什么拦截没生效
拦截器“静默失效”很常见,根本原因是编译器拒绝应用重写。典型原因包括:
-
[InterceptsLocation]路径错误:文件名大小写不符、路径是相对/绝对混淆、行号列号偏移(比如注释占行、IDE 显示行号 vs 实际行号) - 被拦截方法不是
internal或private(例如public成员被其他项目引用) - 拦截器方法和被拦截方法不在同一源文件、或分属不同
partial类但未被编译器视为同一逻辑单元 - 启用了增量编译但缓存未刷新,可尝试
dotnet clean后重试 - 使用了不支持该特性的 SDK 版本(需 .NET 8 SDK RC2+,且项目 SDK 为
Microsoft.NET.Sdk)
它不适合替代 AOP 或日志框架
Interceptors 不是 Castle.DynamicProxy、AspectCore 或 Microsoft.Extensions.DependencyInjection 中的拦截扩展。它没有上下文、无法访问调用栈、不能处理异常后逻辑、也不支持条件拦截(比如只拦截特定参数值)。它的定位非常窄:用于极底层、确定性替换,比如:
- 在测试中静态替换某个配置读取方法,避免启动完整 DI 容器
- 在发布构建中将调试日志调用直接替换为空实现
- 为特定平台 API 提供编译期 fallback 实现(如 Windows-only 方法在 Linux 下替换为 stub)
一旦你想到“我需要在方法执行前后做点什么”,那它就不是合适工具——这时候该用 Source Generators、DiagnosticAnalyzer,或者老老实实写装饰器。










