Skip to main content

How to Resolve Error "CS0065: event property must have both add and remove accessors" in C#

The Compiler Error CS0065 is a syntax error regarding Custom Event Declarations. The message reads: "event property must have both add and remove accessors".

In C#, when you declare an event, you typically use the "field-like" syntax (e.g., public event Action OnClick;), and the compiler automatically generates the subscription logic for you. However, if you choose to define the event manually (to add custom logging, thread safety, or wrapper logic), you must explicitly define both the add and remove blocks. You cannot implement one without the other.

This guide explains how to correctly implement custom event accessors.

Understanding Event Accessors

Events in C# work similarly to Properties.

  • A Property has get and set.
  • An Event has add (called when using +=) and remove (called when using -=).

While properties allow you to be "Read Only" (get-only) or "Write Only" (set-only), events typically require symmetry. If you allow a user to subscribe (+=), you generally must allow them to unsubscribe (-=) to prevent memory leaks. Therefore, the compiler enforces that if you manually define the event body, you must provide both accessors.

Scenario: The Missing Accessor

This error usually happens when a developer tries to add custom logic (like logging) to an event subscription but forgets to handle the unsubscription.

Example of problem:

using System;

public class Button
{
// ⛔️ Error CS0065: 'Button.Click' : event property must have
// both add and remove accessors.
public event EventHandler Click
{
add
{
Console.WriteLine("Someone subscribed!");
// (Internal logic to store the delegate would go here)
}
// Missing 'remove' block!
}
}

Solution 1: Use Field-Like Events (Automatic)

If you do not need custom logic (like thread locking or logging), simply remove the curly braces. This is called a Field-like Event. The compiler will automatically generate a private backing field and the default add/remove implementations for you.

Solution:

using System;

public class Button
{
// ✅ Correct: No body. The compiler generates add/remove automatically.
public event EventHandler Click;
}

Solution 2: Implement Both Accessors (Manual)

If you specifically need custom logic (for example, wrapping another event or managing a private delegate manually), you must implement both add and remove.

Solution: You generally need a private backing field to store the delegates.

using System;

public class Button
{
// Private field to hold the subscribers
private EventHandler _click;

// ✅ Correct: Both add and remove are defined
public event EventHandler Click
{
add
{
Console.WriteLine("Subscriber added.");
// Combine the new subscriber with the existing list
_click = (EventHandler)Delegate.Combine(_click, value);
}
remove
{
Console.WriteLine("Subscriber removed.");
// Remove the subscriber from the list
_click = (EventHandler)Delegate.Remove(_click, value);
}
}

// Method to trigger the event
public void SimulateClick()
{
_click?.Invoke(this, EventArgs.Empty);
}
}
note

Common Use Case: Custom accessors are frequently used in UI libraries (like WinForms) to store events in a EventHandlerList to save memory, rather than having a separate field for every possible event.

Conclusion

CS0065 ensures that your events behave predictably.

  1. Default Behavior: If you just need a standard event, do not use curly braces. End the line with a semicolon.
  2. Custom Behavior: If you open the curly braces { }, you are responsible for the entire implementation. You must provide both add and remove.