> ## Documentation Index
> Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt
> Use this file to discover all available pages before exploring further.

# C# Event

An event in C# is a class or struct member that provides notifications to subscribers. Declared using the `event` modifier, it provides an encapsulation layer over a multicast delegate, ensuring that external entities can only subscribe (`+=`) or unsubscribe (`-=`) from the delegate, while strictly confining the invocation privilege to the declaring type.

Without the `event` modifier, a public delegate field could be invoked, reassigned, or cleared by any external class. The `event` keyword enforces access control, preventing external code from breaking the invocation chain.

## Compiler Mechanics

The underlying implementation of an event depends on its declaration syntax. For standard, *field-like* events, the C# compiler automatically generates three components under the hood:

1. **A private delegate backing field:** This stores the invocation list of subscribed methods.
2. **An `add` accessor:** A method that safely appends a delegate to the backing field's invocation list using `Delegate.Combine`.
3. **A `remove` accessor:** A method that safely removes a delegate from the backing field's invocation list using `Delegate.Remove`.

By default, the compiler-generated `add` and `remove` accessors use thread-safe operations (via `Interlocked.CompareExchange`) to prevent race conditions during concurrent subscription modifications.

If the event is explicitly implemented (using custom accessors), the compiler only generates the `add` and `remove` methods. It does not generate a backing field, leaving the developer responsible for defining and managing the underlying delegate storage.

## Standard Event Syntax

The most common way to declare an event is using a field-like syntax. The standard .NET convention utilizes the built-in `EventHandler` delegate when no custom event data is passed, and `EventHandler<TEventArgs>` when passing custom event arguments.

```csharp theme={"dark"}
public class Publisher
{
    // 1. Event declaration using standard non-generic EventHandler
    public event EventHandler OperationCompleted;

    // 2. Protected virtual method to handle invocation (Standard .NET Pattern)
    protected virtual void OnOperationCompleted(EventArgs e)
    {
        // Null-conditional operator prevents NullReferenceException 
        // if the invocation list is empty (no subscribers).
        OperationCompleted?.Invoke(this, e);
    }

    public void DoWork()
    {
        // Internal logic here...
        OnOperationCompleted(EventArgs.Empty);
    }
}

public class Subscriber
{
    public void Subscribe(Publisher publisher)
    {
        // External classes can ONLY use += or -=
        publisher.OperationCompleted += HandleOperationCompleted;
    }

    private void HandleOperationCompleted(object sender, EventArgs e)
    {
        // Callback execution logic
    }
}
```

## Explicit Event Accessors

If you need to control the underlying storage of the delegate (e.g., to optimize memory when a class exposes dozens of events but only a few are subscribed to), you can explicitly implement the `add` and `remove` accessors. This is syntactically similar to property getters and setters.

When implementing custom thread safety, a dedicated private locking object must be used to prevent deadlocks, avoiding anti-patterns like locking on `this`.

```csharp theme={"dark"}
public class CustomAccessorPublisher
{
    // Dedicated locking object to prevent deadlocks
    private readonly object _eventLock = new object();
    
    // Explicit backing field
    private EventHandler _operationCompleted;

    // Explicit event declaration
    public event EventHandler OperationCompleted
    {
        add
        {
            // Custom logic (e.g., custom locking, logging, or dictionary storage)
            lock (_eventLock)
            {
                _operationCompleted += value;
            }
        }
        remove
        {
            lock (_eventLock)
            {
                _operationCompleted -= value;
            }
        }
    }

    protected virtual void OnOperationCompleted(EventArgs e)
    {
        _operationCompleted?.Invoke(this, e);
    }
}
```

## Memory Management Implications

Events inherently create a strong reference from the publisher to the subscriber. When a subscriber registers a method to an event, the publisher's delegate backing field holds a reference to the subscriber's instance (via the delegate's `Target` property).

If the publisher's lifecycle outlives the subscriber, the garbage collector cannot reclaim the subscriber's memory until the publisher is also collected or the subscriber explicitly unsubscribes using the `-=` operator. This is the most common cause of memory leaks in .NET applications.

<div
  style={{ 
display: "flex", 
justifyContent: "space-between", 
alignItems: "center", 
maxWidth: "754px", 
padding: "1rem 0",
marginBottom: "24px"
}}
>
  <span style={{ fontWeight: "bold", fontSize: "1.25rem", color: "var(--tw-prose-headings)", fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif" }}>Tired of Poor C# Skills? Fix That With Deep Grasping!</span>

  <a
    href="https://syntblaze.com"
    target="_blank"
    style={{ 
  marginLeft: "24px",
  textDecoration: "none", 
  backgroundColor: "#007AFF",
  color: "#ffffff", 
  padding: "6px 16px", 
  borderRadius: "16px",
  fontSize: "0.9rem",
  fontWeight: "600",
  textAlign: "center",
  transition: "background-color 0.2s ease"
}}
  >
    Learn More
  </a>
</div>

<div style={{ display: "flex", gap: "12px", flexWrap: "wrap" }}>
  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/skill-tracking.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=b9b0305c93bb501c9e767b5c76c88835" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/skill-tracking.png" />

  <img src="https://mintcdn.com/syntblazellc/23tyuOzaWS88qFlc/images/nuggets.png?fit=max&auto=format&n=23tyuOzaWS88qFlc&q=85&s=c86c80197299762989e9b882419b2109" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/nuggets.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/bite-sized-exercises.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=a65f9a38c37ff28ab73ed783c53c60e3" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/bite-sized-exercises.png" />
</div>

<div style={{ display: "flex", gap: "12px", flexWrap: "wrap", marginTop: "12px" }}>
  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/mastery-chain.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=748a1763454713e679260fbb95f154a2" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/mastery-chain.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/element-previews.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=242f61448ff5dd6deaaab2dccc13b507" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/element-previews.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/element-explanations.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=cf0fc1c31f9cd0fc26716781be05fbc9" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/element-explanations.png" />
</div>
