partial类必须所有部分都加partial关键字,否则编译报错;仅同一程序集内有效,跨程序集无法拆分;partial方法无实现则被编译器静默移除。
partial 类必须所有部分都加 partial 关键字
不加就不是部分类,编译器直接报错:type 'x' already defines a member called 'y' with the same parameter types。c# 不会自动合并两个同名类,哪怕它们在不同文件、内容完全不冲突——缺一个 partial,就是两个独立类,命名冲突立马触发。
实操建议:
- 每个文件里的类声明都得写成
public partial class MyClass,不能只在主文件写,其他文件漏掉 - 继承关系、泛型参数、接口实现必须**全部写在同一个部分里**(通常放在主文件),其他部分只能加成员,不能重复声明基类或
where约束 - 如果用了
record或struct,同样要写partial record/partial struct,语法一致
生成代码和手写代码混在同一个 partial 类里很常见
这是 partial 最实际的用途:比如 WinForms 的设计器生成 Form1.Designer.cs,你写业务逻辑在 Form1.cs,两者通过 partial 合并为一个完整类。Razor Pages 的 .cshtml.cs 和后台类也这么玩。
容易踩的坑:
- 别在生成文件里手动改代码——下次设计器重刷就全丢了;所有自定义逻辑必须写在非生成文件里
- 两个部分里定义了同名私有字段(比如都写了
private string _cache;),编译不报错但运行时行为不可控,因为实际是两个字段 - 构造函数只能在一个部分里定义;想初始化生成类里的控件?得用
InitializeComponent()调用后的时间点,而不是构造函数体开头
Partial 方法必须有实现才能被调用
partial 方法默认是隐式 private,且**没有实现时会被编译器整个移除**——连签名都不留。所以你在 A 文件声明 partial void OnSaving();,在 B 文件没写实现,那调用它的代码会被静默删掉,不会报错也不会运行。
这特性常用于框架扩展点(比如 Entity Framework Core 的 OnModelCreatingPartial),但要注意:
- 声明和实现的签名必须完全一致:返回类型、名称、参数类型、
ref/out修饰符,一个字母都不能差 - 不能有访问修饰符(
public、protected都不行),也不能是virtual、override、extern - 如果忘了写实现,又在别处调用了它,代码看起来“没执行”,其实根本没编译进去
跨程序集无法拆分 partial 类
partial 只在**同一个程序集(assembly)内有效**。A.dll 里定义了 partial class DataProcessor,B.dll 想用另一个 partial 文件去扩展它?编译器直接无视,当成全新类处理。
替代方案有限:
- 用继承:B.dll 写
class ExtendedDataProcessor : DataProcessor,但破坏了“同一类型”的语义 - 用源生成器(Source Generator)在编译期注入代码,但开发成本高,且需控制生成时机
- 老老实实用组合:把可拆部分抽成独立类,主类持有一个
public IDataHelper Helper { get; }
很多人卡在这一步才意识到:partial 不是“分布式类”,它只是编译器的一个文本合并指令,作用域严格限定在单个 .csproj 编译上下文里。










