Start Debugging
2025-04-08 Updated 2025-04-12 csharp-14csharpdotnetdotnet-10

Partial constructors and events in C# 14

C# 14 lets you declare instance constructors and events as partial members, splitting definitions across files for cleaner code generation and separation of concerns.

C# 14 introduces a new capability to declare instance constructors and events as partial members. This means you can split a constructor or event definition across two parts of a partial class, similar to how C# has long allowed partial methods and partial properties. One part of a partial class can declare a constructor or event, and another part can implement it. This is especially useful in scenarios like code generation, where one file might be auto-generated and another maintained manually by a developer.

Partial constructors in C# 14

Partial constructors let you split a class constructor into two declarations within a partial class. One declaration provides just the signature (a defining declaration), and another provides the actual body (an implementing declaration). The compiler merges these so they behave as a single constructor at runtime.

Key rules for partial constructors:

Below is an example of a partial constructor in action. Imagine a class split across two files – one auto-generated part declares a constructor, and another part implements it:

// File: Car.AutoGenerated.cs (auto-generated partial class)
public partial class Car
{
    // Defining partial constructor (signature only, no body)
    public partial Car(string model);
}

// File: Car.cs (developer-maintained partial class)
public partial class Car
{
    // Implementing partial constructor (actual body)
    public partial Car(string model)
    {
        // (Optional) Can call another constructor or base constructor here
        // : base() or : this(...) would be placed after the parameter list if needed.
        
        if (string.IsNullOrEmpty(model))
            throw new ArgumentException("Model must be provided", nameof(model));
        Console.WriteLine($"Car model set to {model}");
    }
}

In this example, the Car class’s constructor is split: the first part declares that Car(string model) exists, and the second part provides the implementation that checks the argument and prints a message. The defining declaration cannot include any code – it’s just the signature ending in ;. The implementing declaration has the body and could also include a base class call (if Car inherited from another class) or call another constructor via this(). At compile time, the compiler ensures there is exactly one of each part and then treats the constructor as a single unified constructor.

Partial events in C# 14

Partial events allow a similar split definition for events in a partial class. One part of the class defines the event (with no add/remove logic, just like a normal automatic event declaration), and another part provides the add/remove accessors (the code that runs when subscribers attach or detach event handlers).

Key rules for partial events:

Here’s an example demonstrating a partial event. One file declares the event, and another file implements custom add/remove logic:

// File: Sensor.AutoGenerated.cs (auto-generated partial class)
public partial class Sensor
{
    // Defining partial event (no body, just declaration)
    public partial event EventHandler DataReceived;
}

// File: Sensor.cs (developer-maintained partial class)
public partial class Sensor
{
    // Implementing partial event (with custom add/remove logic)
    private EventHandler _dataReceivedHandlers;  // backing storage for event handlers

    public partial event EventHandler DataReceived
    {
        add 
        {
            Console.WriteLine("Subscriber added to DataReceived");
            _dataReceivedHandlers += value;
        }
        remove 
        {
            Console.WriteLine("Subscriber removed from DataReceived");
            _dataReceivedHandlers -= value;
        }
    }

    // Optional: a method to raise the event safely from this class
    protected void OnDataReceived(EventArgs e)
    {
        _dataReceivedHandlers?.Invoke(this, e);
    }
}

In this example, the Sensor class has a DataReceived event split across two files. The auto-generated part of the class declares that there is a DataReceived event of type EventHandler. The developer’s part provides the actual implementation: it defines a private field to hold the subscribers and implements the add and remove blocks to log a message and update that private field. When code elsewhere does sensor.DataReceived += Handler;, it will run the custom add logic. Likewise, unsubscribing triggers the custom remove logic. The class can use _dataReceivedHandlers to raise the event (for example, in the OnDataReceived method). Without the implementing part, the defining declaration alone wouldn’t compile, since the event has no backing by itself – C# requires the implementing part to complete the event’s functionality.

Comparison with earlier C# versions

Prior to C# 14, you could not declare constructors or events as partial. In C# 13 and earlier, the partial keyword was only valid on classes, methods, and (from C# 13) properties/indexers. If you attempted to mark a constructor or an event as partial, the compiler would produce an error. This limitation meant there was no way to split the implementation of a constructor or an event between different files.

Developers often used workarounds to achieve similar flexibility. For example, consider a scenario where auto-generated code needs to call some custom logic during object construction. Without partial constructors, one common pattern was to use a partial method inside the constructor. The auto-generated part of a class could call this method, while the method’s implementation would be in another file. For instance, in older C# code you might see:

// Before C# 14: using a partial method to extend constructor logic
public partial class Car
{
    public Car()
    {
        InitializeComponents();       // auto-generated initialization
        OnConstructed();              // call a partial method hook
    }

    // This partial method can be implemented in another file to add custom behavior
    partial void OnConstructed();
}

In the second file, the developer could implement the OnConstructed partial method to execute additional code after InitializeComponents(). This pattern allowed injecting custom code at construction time, but it was indirect and a bit clunky. Similarly, for events, there was no easy workaround – one would either have to inherit and override functionality or modify the generated code, since you couldn’t split an event’s add/remove logic across files.

With C# 14’s partial constructors and events, these workarounds are no longer necessary. You can directly declare a constructor or event as partial and supply the implementation separately, making the code more straightforward. The new approach is clearer and eliminates the need for dummy partial methods or other tricks.

Use cases and benefits

The primary beneficiaries of partial constructors and events are scenarios involving code generation and tooling. Many frameworks and tools generate C# code (for example, UI designers, ORMs, or interface scaffolding tools) and often mark classes as partial so that developers can extend the generated classes without editing the auto-generated code. With C# 14:

In all these cases, partial constructors and events make code easier to maintain. The auto-generated code can declare what needs to exist, and the hand-written code can be limited to what the developer really cares about implementing. There’s also no runtime performance cost for using partial members – the separation exists only at compile time. The resulting program behaves as if you wrote the full constructor or event in one place.

By allowing instance constructors and events to be partial, C# 14 rounds out the partial class feature set to cover nearly all member types. This enhancement improves the language’s ability to support clean separation of auto-generated and custom code. Developers get the flexibility to organize and extend class behavior without hacks, and source generators gain a powerful new tool to inject functionality in a modular way. Whether you’re working on large frameworks or just splitting code for clarity, partial constructors and events offer a more expressive and convenient approach in C# 14.

Sources

< Back