Partial types have been part of C# for many years, enabling large classes, records, or structs to be split across multiple files. This capability is heavily used by tooling such as code generators, designers, and source generators.
Until now, partial support has been limited to events and constructors. Developers often had to rely on workarounds, base classes, or manual code merging.
C# 14 introduces partial events and partial constructors, making it easier to combine generated code and hand-written code in a clean, maintainable, and safe way.
1. The Problem Before C# 14
Partial Types with Limitations
Consider a partial class used with code generation:
public partial class OrderService
{
}
While methods and properties could be split across files, constructors and events had to be fully defined in a single place .
This caused issues such as:
Code generators overwriting custom logic
Developers modifying generated files
Poor separation of concerns
2. Partial Constructors in C# 14
C# 14 allows constructors to be declared as partial , enabling their implementation to be split across multiple files.
Basic Partial Constructor Example
File 1 : Generated code
public partial class OrderService
{
partial void Initialize();
public OrderService()
{
Initialize();
}
}
File 2 : Developer-written code
public partial class OrderService
{
partial void Initialize()
{
// Custom initialization logic
Console.WriteLine("OrderService initialized.");
}
}
This pattern ensures:
Generated code remains untouched
Custom logic is safely injected
Clear separation of responsibilities
Partial Constructor with Dependencies
Partial constructors are especially useful when dependency initialization is split between generated and manual code.
File 1 :
public partial class CustomerService
{
private readonly ILogger<CustomerService> _logger;
public CustomerService(ILogger<CustomerService> logger)
{
_logger = logger;
OnConstructed();
}
partial void OnConstructed();
}
File 2:
public partial class CustomerService
{
partial void OnConstructed()
{
_logger.LogInformation("CustomerService constructed.");
}
}
This avoids forcing all initialization logic into a single file.
3. Partial Events in C# 14
C# 14 also introduces support for partial events , allowing event declarations and implementations to be split across partial type definitions.
Motivation for Partial Events
Events are commonly used in:
Domain-driven design
UI frameworks
Workflow engines
Generated models
Previously, events had to be fully declared in one place, limiting extensibility.
Basic Partial Event Example
File 1 : Generated code
public partial class PaymentProcessor
{
public partial event EventHandler PaymentCompleted;
protected void RaisePaymentCompleted()
{
PaymentCompleted?.Invoke(this, EventArgs.Empty);
}
}
File 2 : Developer-written code
public partial class PaymentProcessor
{
public partial event EventHandler PaymentCompleted
{
add
{
Console.WriteLine("Subscriber added.");
_paymentCompleted += value;
}
remove
{
Console.WriteLine("Subscriber removed.");
_paymentCompleted -= value;
}
}
private EventHandler _paymentCompleted;
}
This allows developers to customize event behavior without modifying generated code.
4. Real-World Use Cases
Partial events and constructors are particularly useful in the following scenarios:
Source generators
ORM-generated entities
API client generators
UI frameworks
Enterprise domain models
5. Combining Partial Constructors and Events
A realistic example combining both features:
File 1 :
public partial class InventoryService
{
public partial event EventHandler StockUpdated;
public InventoryService()
{
OnInitialized();
}
partial void OnInitialized();
protected void NotifyStockUpdated()
{
StockUpdated?.Invoke(this, EventArgs.Empty);
}
}
File 2 :
public partial class InventoryService
{
partial void OnInitialized()
{
Console.WriteLine("InventoryService ready.");
}
public partial event EventHandler StockUpdated
{
add
{
Console.WriteLine("StockUpdated handler added.");
_stockUpdated += value;
}
remove
{
_stockUpdated -= value;
}
}
private EventHandler _stockUpdated;
}
This pattern is clean, extensible, and generator-friendly.
6. Comparison with Older Approaches
Older Patterns
Base classes for extensibility
Virtual methods
Manual merge of generated code
Partial methods with limited usage
C# 14 Partial Events and Constructors
No inheritance required
Clear intent
Better tooling support
Safer code generation
7. Performance Considerations
Partial events and constructors:
Introduce no runtime overhead
Are resolved at compile time
Do not affect event invocation performance
Example:
// Event invocation cost remains unchanged
StockUpdated?.Invoke(this, EventArgs.Empty);
The partial nature affects code organization, not runtime behavior.
8. Best Practices
Keep generated and manual code in separate files.
Use partial constructors for initialization hooks.
Use partial events for extensibility points.
Avoid complex logic in generated files.
Clearly document extension points.
9. Common Mistakes to Avoid
Adding business logic directly to generated files.
Overusing partial types when simpler designs suffice.
Mixing responsibilities across partial definitions.
Forgetting to provide default implementations when required.
Conclusion
Partial events and constructors in C# 14 significantly improve how developers structure extensible and generator-friendly code. They remove long-standing limitations and provide a clean, maintainable way to combine generated and hand-written logic.
For teams working with modern tooling, source generators, and large codebases, these features enable better separation of concerns and safer evolution of code over time.
Happy Coding!
I write about modern C#, .NET, and real-world development practices. Follow me on C# Corner for regular insights, tips, and deep dives.