Partial Class Pattern
Split a single class definition across multiple source files to separate auto-generated code from hand-written code, or to organize large classes by concern.
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Partial Class Pattern
Overview
The Partial Class Pattern splits a single class definition across multiple source files. At compile time, the fragments are merged into a single type. This separation allows auto-generated code (from designers, ORMs, or code generators) to live in one file while hand-written customizations live in another, without the risk of one overwriting the other.
While most associated with C# (partial class), the concept exists in other forms: Ruby modules reopen classes, Python allows monkey-patching, and Java’s default interface methods plus records achieve similar goals. The core benefit is organizational separation without runtime overhead.
When to Use
Use the Partial Class Pattern when:
- Code generators produce large boilerplate that should not be manually edited
- Hand-written business logic should be kept separate from scaffolded code
- Multiple developers work on different aspects of the same class simultaneously
- You want to organize a large class by concern (persistence, validation, serialization)
When to Avoid
- The class is small enough to fit comfortably in a single file
- The splits create circular dependencies or confusing navigation
- Language does not support partial types natively (workarounds add complexity)
- The pattern is used to hide that a class has grown too large (refactor instead)
Solution
C# (Native)
// AutoGenerated.cs — generated by a tool, do not edit
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
// Customer.Custom.cs — hand-written business logic
public partial class Customer
{
public bool IsValidEmail()
{
return Email?.Contains("@") ?? false;
}
public string GetDisplayName()
{
return $"{Name} <{Email}>";
}
}
Python
Python does not have partial classes, but classes can be reopened and monkey-patched:
from dataclasses import dataclass
# File: customer_base.py (auto-generated)
@dataclass
class Customer:
id: int
name: str
email: str
# File: customer_custom.py (hand-written)
def is_valid_email(self) -> bool:
return "@" in self.email if self.email else False
def get_display_name(self) -> str:
return f"{self.name} <{self.email}>"
# Reopen the class by attaching methods
Customer.is_valid_email = is_valid_email
Customer.get_display_name = get_display_name
# Usage
customer = Customer(id=1, name="Alice", email="alice@example.com")
print(customer.is_valid_email()) # True
print(customer.get_display_name()) # Alice <alice@example.com>
Java
Java does not have partial classes, but nested classes, default interface methods, and records with builders achieve similar separation:
// Auto-generated base
public class CustomerBase {
private final int id;
private final String name;
private final String email;
public CustomerBase(int id, String name, String email) {
this.id = id; this.name = name; this.email = email;
}
public int getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
}
// Hand-written extension via inheritance
public class Customer extends CustomerBase {
public Customer(int id, String name, String email) {
super(id, name, email);
}
public boolean isValidEmail() {
return getEmail() != null && getEmail().contains("@");
}
public String getDisplayName() {
return getName() + " <" + getEmail() + ">";
}
}
// Usage
Customer customer = new Customer(1, "Alice", "alice@example.com");
System.out.println(customer.isValidEmail());
System.out.println(customer.getDisplayName());
JavaScript
JavaScript allows extending prototypes at any time, achieving partial-class behavior:
// File: customerBase.js (auto-generated)
class Customer {
constructor(id, name, email) {
this.id = id;
this.name = name;
this.email = email;
}
}
// File: customerCustom.js (hand-written)
Customer.prototype.isValidEmail = function () {
return this.email?.includes('@') ?? false;
};
Customer.prototype.getDisplayName = function () {
return `${this.name} <${this.email}>`;
};
// Usage
const customer = new Customer(1, 'Alice', 'alice@example.com');
console.log(customer.isValidEmail()); // true
console.log(customer.getDisplayName()); // Alice <alice@example.com>
Explanation
The Partial Class Pattern solves a tooling and maintenance problem:
- Before: Code generators overwrite hand-written changes every time they run
- After: Generated code lives in one file, custom code in another; both compile to a single type
This is not about runtime behavior (there is no runtime difference between a partial and a monolithic class), but about source code organization and generator safety.
Variants
| Variant | Language | Mechanism |
|---|---|---|
| Partial class | C#, VB.NET | Native partial keyword |
| Reopen class | Ruby, Python | Monkey-patch / reopen at runtime |
| Mixin modules | Ruby, Python | Include modules into class |
| Default interface methods | Java | Interface methods with bodies |
| Partial method | C# | Generated stub, optional implementation |
Best Practices
- Never edit generated files. Mark them with
// <auto-generated>comments. - Use consistent naming.
Customer.generated.csandCustomer.custom.csmake intent clear. - Keep partials cohesive. Split by concern (serialization, validation), not arbitrarily.
- Document which file contains what. A class-level comment helps navigation.
- Regenerate in CI. Ensure generated files are always fresh and not manually modified.
Common Mistakes
- Splitting arbitrarily. Five partial files for a 100-line class is overkill.
- Creating circular dependencies. Partials should not depend on each other in confusing ways.
- Mixing generated and hand-written code in the same file. Defeats the purpose.
- Using partials to avoid refactoring. If a class needs ten partials, it may need extraction into smaller classes.
- Assuming thread safety from splitting. Partial classes have the same threading semantics as regular classes.
Real-World Examples
WinForms / WPF Designers (C#)
Visual Studio’s Windows Forms designer generates Form1.Designer.cs with auto-generated control initialization, while Form1.cs contains event handlers and business logic.
Entity Framework (C#)
EF’s scaffolding tools generate partial entity classes, allowing developers to add validation, computed properties, and business logic in separate partial files that survive re-scaffolding.
ASP.NET Core Razor
Razor pages compile markup (.cshtml) and code-behind (.cshtml.cs) into a single partial class, separating presentation from logic.
Frequently Asked Questions
Q: What is the difference between Partial Class and Inheritance? A: Partial classes are merged at compile time into a single type. Inheritance creates a runtime relationship between two distinct types. Partial classes cannot add fields that other partials depend on at initialization.
Q: Can partial classes have different access modifiers? A: No, all partial declarations must have the same access modifier and must be in the same assembly/namespace.
Q: How do I achieve partial classes in Java or Python? A: Java uses composition or inheritance; Python uses monkey-patching or mixins. Neither has true compile-time partials like C#.
Q: Can partial methods exist without an implementation? A: In C#, yes. A partial method declaration can exist in the generated file without an implementation in the custom file. The compiler removes the call site entirely if no implementation exists, producing zero overhead.