c# 12 引入 file-local 类型,需显式设置 langversion 为 12.0 或 latest;file 类型仅在声明文件内可见,不可跨文件访问、继承或实现,编译后 il 无差异但元数据 isvisible=false,反射也无法跨文件发现。

file-local 类型在 C# 12 中才正式支持
你无法在 C# 12 之前使用 file 访问修饰符声明类型。如果编译器报错 error CS8904: Feature 'file-scoped type' is not available in C# 11. Please use language version 12 or greater,说明项目语言版本太低。必须显式升级到 C# 12 或更高版本——仅安装新 SDK 不够,还需在 .csproj 中指定:
<LangVersion>12.0</LangVersion>或设为
latest。
用 file class 声明文件内私有类型
语法很简单:把 file 关键字放在 class(或 struct、interface、delegate)前即可。它不接受其他访问修饰符(如 public、internal),也不允许被继承或实现(除非在同一文件中)。
常见误用点:
- 试图在另一文件中
new这个类型 → 编译失败,提示The type 'X' is not accessible due to its protection level - 在同文件中写
public class A : file class B→ 允许;但跨文件继承B→ 不允许,报error CS0260: Missing partial modifier on declaration of 'B'; another partial declaration of this type exists(实际是语义错误,编译器用 partial 错误误导你) - 声明
file interface I后,在另一文件中写class C : I→ 编译失败,接口不可见
和 internal + 文件命名空间的差异
file 类型比 internal 更严格:后者在同一程序集任意文件都可见;前者只在声明它的 .cs 文件内有效,哪怕两文件共享同一根命名空间也互不可见。
性能与生成无区别——编译后仍是常规 IL 类型,只是元数据中 IsVisible 为 false,且 C# 编译器在语义分析阶段就拦截了跨文件引用。
适用场景包括:
- 封装仅用于当前文件内某组方法的辅助类(比如 JSON 转换时的临时 DTO)
- 避免污染命名空间:不用再起类似
MyService_TempHelper这种丑名字 - 替代局部函数无法承载的复杂逻辑(局部函数不能有字段、不能实现接口、不能被反射获取)
file-local 类型不能被反射跨文件发现
即使你用 Assembly.GetExecutingAssembly().GetTypes(),也不会包含其他文件里的 file 类型——它们根本不在该程序集的公开类型列表中。但同一文件内的代码仍可通过 typeof(MyFileLocalClass) 获取 Type 对象。
注意一个边界情况:若在文件 A 中定义 file class AHelper,又在文件 A 中用 typeof(AHelper).Assembly.GetTypes(),返回数组里**不会**包含 AHelper —— 因为 GetTypes() 只返回 IsVisible == true 的类型,而 file 类型总是 IsVisible == false。
所以别指望靠反射“绕过”作用域限制;这是编译期强制约束,不是运行时封装。










