C# Partial Methods: How They Work and Constraints
Partial methods in C# allow developers to declare a method signature in one part of a partial type and optionally implement it in another part or source file. Primarily utilized in code generation and source generators, they provide clean, compile-time hooks for extending generated logic without introducing runtime overhead. This article explains how partial methods work, the distinction between optional and required partial methods, and the strict constraints that govern their implementation.
How Partial Methods Work
A partial method consists of two parts: the definition (the method
signature) and the implementation (the method body). Both parts must
reside within a partial type, such as a
partial class, partial struct, or
partial interface.
Depending on how they are declared, partial methods function in one of two modes:
1. Optional Partial Methods (Classic)
Introduced in C# 3.0, optional partial methods act as lightweight hooks. If a defining declaration is present but no implementing declaration is provided:
- The C# compiler removes the method signature during compilation.
- All calls to the method are completely eliminated from the generated Intermediate Language (IL).
- Any arguments evaluated solely for the method call are also stripped, avoiding performance penalties.
public partial class OrderProcessor
{
// Defining declaration
partial void OnOrderProcessed(int orderId);
public void ProcessOrder(int orderId)
{
// If OnOrderProcessed is not implemented, this call is erased at compile time
OnOrderProcessed(orderId);
}
}2. Extended Partial Methods (Required)
Starting with C# 9.0 to support Roslyn Source Generators, partial
methods can be extended to require an implementation. If an
accessibility modifier (such as public,
internal, or an explicit private) is provided
on the defining signature:
- The method must have an implementation somewhere within the partial type.
- The method is not erased at compile time.
- It can return values and participate in standard object-oriented patterns.
public partial class UserValidator
{
// Defining declaration (requires an implementation because it has an access modifier)
public partial bool ValidateUsername(string username);
}
public partial class UserValidator
{
// Implementing declaration
public partial bool ValidateUsername(string username)
{
return !string.IsNullOrWhiteSpace(username);
}
}Constraints on Partial Methods
The C# compiler enforces specific rules depending on whether the partial method is optional or required.
Universal Constraints
- Enclosing Types: The method must be declared inside
a
partial class,partial struct,partial interface, orpartial record. - Matching Signatures: The defining and implementing
declarations must match exactly in method name, parameter types, generic
constraints, and parameter modifiers (
ref,in). - Static Modifiers: A partial method can be
static, but both declarations must include thestaticmodifier. - Delegate Creation: You cannot create a delegate pointing to an optional partial method that has no implementation.
Constraints on Optional Partial Methods
If a partial method does not specify an explicit access modifier, it defaults to optional mode and is subjected to the following limitations:
- Return Type: Must return
void. - Accessibility: Cannot have explicit access
modifiers (they are implicitly
private). - Parameters: Cannot have
outparameters, because the compiler cannot guarantee the variable would be assigned if the method call is removed. - Inheritance Modifiers: Cannot use
virtual,abstract,override,new, orsealed. - Extern Modifier: Cannot be marked as
extern.
Constraints on Extended (Required) Partial Methods
If a partial method includes an explicit access modifier, it is considered required:
- Mandatory Implementation: If a definition exists with an access modifier, failing to provide an implementation generates a compile-time error.
- Return Type Flexibility: Can return any type, not
just
void. - Parameters:
outparameters are permitted because the method is guaranteed to exist and execute. - Inheritance Modifiers: Can be marked with
virtual,override,abstract, orsealed, provided the access modifiers and context allow it.