Back to Blog
C#

C# Partial Method Usage and Limitations

c# partial method: Explore C# partial methods: syntax, conditions for implementation, code generation usage, and common pitfalls for .NET developers.

C#partial methodscode generationsource generatorspartial types
A technical illustration showing a C# code file with a partial method declaration connected to an optional implementation block, representing the concept of partial methods.

When working with generated code in C#, you often need a hook that lets a developer customize behavior without forcing them to implement a method. A c# partial method provides exactly that: a method declaration in one part of a partial type and an optional implementation in another. If no implementation exists, the compiler removes the declaration, the call sites, and any associated arguments at compile time. This means partial methods give you the flexibility of an extension point without the runtime cost of an interface or virtual method call when the feature is unused.

Declaration and Definition Rules

A partial method must be declared within a partial class or a partial struct. The declaration uses the partial keyword before the return type. The signature must include the partial keyword, and the method must have a return type of void. Parameters can be ref or in, but not out. Accessibility modifiers are not allowed on the declaration; the method is implicitly private.

The implementation is optional. When you provide it, you must write the same signature with the partial keyword, and it must appear in a different part of the partial type. The implementation can include any accessibility modifier, but it must match the signature of the declaration. If the declaration includes ref or in parameters, the implementation must match them exactly.

// File1.cs public partial class OrderProcessor { partial void BeforeValidate(); } // File2.cs public partial class OrderProcessor { partial void BeforeValidate() { // Custom logic here } }

In this example, BeforeValidate is declared in one file and implemented in another. If the implementation were missing, the compiler would remove the declaration and any call to it. The compiler does not produce an unused method warning because the method effectively does not exist.

When the Compiler Removes the Call

If an implementation is not provided, the compiler removes the method declaration, the call site, and the arguments passed to it. This behavior is what makes partial methods efficient. Consider a call that passes a large object or a string: if no implementation exists, the argument expression is never evaluated.

public partial class Logger { partial void Log(string message); public void ProcessOrder(int id) { Log($"Processing order {id}"); } } // No implementation of Log provided

Because Log has no implementation, the interpolated string $"Processing order {id}" is never constructed. This can save significant overhead in high-frequency code paths when the optional method is not needed. However, this also means you cannot rely on the method existing at runtime; any attempt to inspect it via reflection will fail if the implementation is omitted.

Using Partial Methods in Code Generation

The primary use case for c# partial method is to provide a customizable hook in generated code. Instead of generating a virtual method that a developer can override, a source generator or a designer can emit a partial method declaration, and the developer implements it in a separate file. This avoids the overhead of virtual dispatch and avoids forcing the developer to implement a method they may not need.

For example, a generated data layer might produce a partial class for each entity. The generated code can declare partial methods that fire before or after certain operations, such as validation or property changes. The developer can implement the ones relevant to their business logic and ignore the rest.

The generated code might look like this:

// Generated by a source generator public partial class Customer { partial void OnNameChanged(string oldValue, string newValue); private string _name; public string Name { get => _name; set { var old = _name; _name = value; OnNameChanged(old, value); } } }

A developer using this generated type can implement OnNameChanged in a separate file without modifying the generated code. If they do not implement it, the generated property setter simply assigns the value without any extra work. This keeps the generated code clean and maintainable.

Partial Methods vs. Interfaces vs. Virtual Methods

When you need to provide an extension point, you have several options in C#. Partial methods are not a replacement for virtual methods or interfaces; they serve a different purpose.

FeaturePartial MethodVirtual MethodInterface Method
Implementation requiredNoNo (default)Yes
AccessibilityPrivate only (declaration)AnyPublic
Runtime cost when unimplementedNoneVirtual call overheadCall overhead
Can be sealedNoYesN/A
Suitable for cross-assembly extensibilityNo (must be in same assembly)YesYes

A partial method must be implemented in the same assembly as the declaration because the declaration is implicitly private. If you need to allow extensibility from another assembly, you would use a virtual method, an interface, or possibly an abstract method. Partial methods are best used when the extension point is optional and the generated code is in the same assembly as the consuming developer's code.

Using Partial Methods with Source Generators

Source generators in .NET often employ partial methods to generate boilerplate while allowing user customization. A generator can emit a partial method declaration, and the user provides an implementation. This pattern is common in libraries that generate DTOs, serializers, or mappers.

For example, a source generator might produce a partial method that allows you to customize serialization for a specific type.

// Generated by a source generator public partial class Person { partial void CustomSerialize(JsonWriter writer); } // User code public partial class Person { partial void CustomSerialize(JsonWriter writer) { writer.WriteString("FullName", $"{FirstName} {LastName}"); } }

The source generator can detect whether the user has implemented the method by looking for a partial method implementation during generation. If not present, the generator can omit the call site. This technique allows for zero-cost extensibility when the optional behavior is not needed.

Common Pitfalls and How to Avoid Them

Partial methods are useful, but they carry restrictions that can lead to confusion. Here are several common mistakes developers make with c# partial method code.

Returning a Value

A partial method must have a return type of void. If you need to return a value, you cannot use a partial method. Instead, consider using a partial method with a ref parameter to pass a result back, or restructure the design to use an interface or a virtual method.

// This will not compile: partial methods must return void // partial int ComputeValue();

If you attempt to declare a partial method with a non-void return type, the compiler produces an error like CS0751 because partial methods must have void return type (as of C# 9). This is a hard language rule that has not changed for traditional partial methods.

Using out Parameters

The declaration of a partial method cannot use out parameters. However, ref and in are allowed. The same parameter modifiers must appear in the implementation. This restriction exists because the compiler may remove the method call entirely, and an out parameter would need to be assigned even if the method is not called.

Accessibility Mismatch

The declaration is implicitly private. The implementation can specify private, public, protected, or internal, but the method will still be callable only from within the declaring type. If you attempt to make the implementation public and then call it from outside the type, the call will not compile because the method is still private. The accessibility modifier on the implementation is essentially ignored for callers outside the type.

Forgetting the partial Keyword on One Side

A partial method declaration must have partial on both the declaration and the implementation. Omitting partial on either side results in a compile error. The compiler treats them as separate methods if the keyword is missing, leading to a method without an implementation or a duplicate definition.

Advanced Usage: Partial Properties and Static Partial Methods

Starting with C# 9, partial methods support more flexibility. You can declare partial methods that are static, and you can have partial properties. However, for static partial methods, the same rules apply: return type must be void, and accessibility is private on the declaration.

Partial properties, introduced in C# 13, allow you to define a property with a partial implementation. This is useful for generated code that needs to add custom getter or setter logic.

public partial class WeatherForecast { public partial int TemperatureC { get; set; } } public partial class WeatherForecast { private int _temperatureC; public partial int TemperatureC { get => _temperatureC; set => _temperatureC = value; } }

Note that this feature requires .NET 9 or later and C# 13. If you are targeting an older runtime, you cannot use partial properties. These newer features are an expansion of the original partial method mechanism, but the core behavioral model remains the same: if the implementation is missing, the declaration and call sites are removed.

Compatibility Considerations with Older C# Versions

If you are working in a codebase that targets C# 8 or earlier, partial methods are limited to void return types and cannot be static, async, or use out parameters. In C# 9, these restrictions were relaxed for static and async partial methods, but the void and out restrictions remain for all versions. This is important when you maintain libraries that are consumed by older projects. If you want to use partial methods in a library, you must ensure the consuming project's language version supports any features you rely on.

For example, an async partial method is allowed only in C# 9 or later. If you need to support C# 8, you cannot use async partial methods. This can affect source generators that emit partial methods with asynchronous signatures.

Performance and Maintainability Tradeoffs

The primary performance benefit of partial methods is that an unused extension point costs nothing at runtime. There is no virtual table entry, no null check, and no method call when the implementation is absent. This is particularly valuable in tight loops or frequently called methods where an empty hook would otherwise incur overhead.

However, this efficiency comes at the cost of discoverability. A developer reading a class may see a call to OnNameChanged and assume it runs logic. If the implementation is missing, the call is a no-op, but that is not obvious from the call site. To mitigate this, document the partial method and consider providing a default implementation in a partial class file that is clearly optional.

From a maintainability standpoint, partial methods can fragment logic across multiple files. A developer might need to search for all parts of a partial class to understand the full behavior. Tools like IDEs can help navigate partial type members, but it still requires awareness. Use partial methods judiciously, especially in hand-written code. They shine in generated code, but in ordinary business logic, a simpler approach may be clearer.

Debugging and Observability with Partial Methods

When debugging a codebase that uses partial methods, it is helpful to know whether an implementation exists. In the debugger, you can step into a partial method call. If the method is unimplemented, the debugger will step over it without any indication. This can cause confusion when you are trying to trace why certain behavior is not occurring.

To make partial methods more observable, you can add logging or tracing inside the implementation. If the implementation is missing, no logging occurs, which is expected. If you need to guarantee that some action happens, consider using a virtual method or an interface instead of a partial method.

The compiler reports partial methods in the XML documentation output when you generate documentation, but it does not indicate whether an implementation exists. Reflection cannot be used to check for the presence of a partial method, because an unimplemented declaration is removed from the metadata. This has implications for frameworks that use reflection to discover extension points.

A practical debugging technique is to temporarily add an empty implementation to see if the call site is reached. For example:

partial void Log(string message) { // Intentionally empty, serve as a breakpoint target }

Put a breakpoint inside this implementation, and you will hit it only if the method is called. This can help verify that the call site the compiler generates is where you expect it.

Deprecated and Alternative Patterns

Before partial methods were introduced in C# 3.0, designers and code generators often emitted virtual methods or events to provide extensibility. Virtual methods force a class to be inheritable and add a virtual call overhead, even if the override is empty. Events require subscribing and introduce delegate allocations. Partial methods offer a lighter-weight alternative but are limited to a single implementation per declaration. If you need more than one subscriber, you would use an event instead.

Another alternative is to use a source generator to emit code conditionally based on the presence of a method. Some generators inspect the user's code and generate a call only if the method exists. This is a more advanced technique but gives you full control over the generated output.

In modern .NET, many libraries use source generators to implement the same pattern that partial methods provide, but with more flexibility. Source generators can inspect user code and generate partial method implementations automatically. However, partial methods themselves remain a standard language feature with stable behavior.

The Role of partial Keyword on Methods in C#

The partial keyword on a method is distinct from partial on a class or struct. A partial type allows the type definition to be split across files, but a partial method further allows the method's declaration and implementation to be in different parts. The two concepts are related but separate. You must have a partial type to use a partial method, but you can have a partial type without any partial methods.

The compiler treats a partial method declaration as a contract for an optional implementation. This is different from an abstract method, which requires implementation. It also differs from a virtual method, which provides a default implementation. The uniqueness of partial methods is that the method may not exist at all in the compiled output if there is no implementation.

Concurrency and Partial Methods

Partial methods do not introduce any concurrency concerns by themselves. They are just methods that may be removed. However, if a partial method is implemented and the implementation modifies shared state, you are responsible for thread safety, just as with any other method. There is no built-in synchronization. In a multi-threaded scenario, a partial method that is called from a property setter could be invoked concurrently, so you must protect any mutable state it touches.

c# partial method: Practical Usage and Code Examples | RYUSLOG DEV