Bird
Raised Fist0
C Sharp (C#)programming~15 mins

Custom event arguments in C Sharp (C#) - Deep Dive

Choose your learning style10 modes available
Overview - Custom event arguments
What is it?
Custom event arguments are special classes you create to send extra information when an event happens in C#. Instead of just knowing that something occurred, you can pass details about that event. This helps the event handler understand the context and respond properly. It is a way to make events more useful and informative.
Why it matters
Without custom event arguments, events can only tell that something happened but not what exactly happened or any details about it. This limits how well your program can react to events. Custom event arguments let you send meaningful data with events, making your programs smarter and more interactive. Imagine a fire alarm that only says 'fire' versus one that says 'fire in kitchen at 3rd floor'—the second is much more helpful.
Where it fits
Before learning custom event arguments, you should understand basic events and delegates in C#. After mastering custom event arguments, you can explore advanced event patterns like event bubbling, asynchronous events, or using event arguments in UI frameworks like WPF or WinForms.
Mental Model
Core Idea
Custom event arguments are like personalized message envelopes that carry extra details along with an event notification.
Think of it like...
Imagine sending a letter (event) to a friend. A custom event argument is like adding a detailed note inside the envelope explaining why you are writing, so your friend knows exactly what to do when they read it.
Event Publisher ──► Event Raised ──► Custom Event Args ──► Event Handler

┌───────────────┐      ┌───────────────┐      ┌────────────────────┐      ┌───────────────┐
│ Publisher     │─────▶│ Event Raised  │─────▶│ Custom Event Args   │─────▶│ Event Handler │
└───────────────┘      └───────────────┘      └────────────────────┘      └───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding basic events in C#
🤔
Concept: Learn what events are and how they notify when something happens.
In C#, an event is a way for one part of a program to tell another part that something happened. For example, a button click event tells your program when the button is pressed. Events use delegates, which are like pointers to methods that will run when the event occurs.
Result
You can create and subscribe to simple events that notify without extra data.
Understanding events as notifications is the foundation for adding more detailed information later.
2
FoundationUsing EventArgs for basic event data
🤔
Concept: Learn about the built-in EventArgs class used to send simple event data.
The EventArgs class is the base class for event data in C#. It is empty and used when no extra information is needed. Events often have a signature like void Handler(object sender, EventArgs e). This means the event sends the sender and some event data (which can be empty).
Result
You can handle events that only tell that something happened, without details.
Knowing EventArgs is the default event data type helps you see why custom event arguments are needed for more info.
3
IntermediateCreating a custom event arguments class
🤔Before reading on: do you think you can add any data you want to an event by making a new class? Commit to your answer.
Concept: Learn how to define your own class that inherits from EventArgs to carry custom data.
To send extra information with an event, create a class that inherits from EventArgs. Add properties to this class for the data you want to send. For example, a TemperatureChangedEventArgs class might have a property for the new temperature value.
Result
You have a new event argument class that can carry specific data when the event fires.
Understanding inheritance from EventArgs is key to making custom event data that fits your needs.
4
IntermediateRaising events with custom arguments
🤔Before reading on: do you think you must create a new delegate type to use custom event arguments? Commit to your answer.
Concept: Learn how to raise events using your custom event arguments and the standard EventHandler delegate.
Instead of creating a new delegate, use the generic EventHandler where T is your custom event args class. When raising the event, create an instance of your custom args with the data and pass it to the event handler. This keeps your code clean and consistent.
Result
Your event can now send detailed information to subscribers using your custom event arguments.
Knowing about EventHandler simplifies event declaration and usage with custom data.
5
AdvancedSubscribing and handling custom event arguments
🤔Before reading on: do you think event handlers must cast EventArgs to your custom type manually? Commit to your answer.
Concept: Learn how to write event handlers that receive and use your custom event arguments safely and cleanly.
When subscribing to an event with custom arguments, your handler method should match the signature void Handler(object sender, YourCustomEventArgs e). This way, you get direct access to the custom properties without casting. Use the data inside the handler to respond appropriately.
Result
Event handlers can access detailed event data easily and respond accordingly.
Matching handler signatures to custom event args avoids errors and makes code clearer.
6
AdvancedBest practices for custom event arguments
🤔Before reading on: do you think event argument classes should be mutable or immutable? Commit to your answer.
Concept: Learn how to design your custom event argument classes for safety and clarity.
It is best to make your custom event argument classes immutable after creation. Use readonly properties and set values only in the constructor. This prevents accidental changes to event data after the event is raised, making your program more reliable.
Result
Your event data is safe from unintended changes, improving program stability.
Understanding immutability in event args prevents bugs caused by changing event data unexpectedly.
7
ExpertAdvanced event argument patterns and pitfalls
🤔Before reading on: do you think passing large data in event arguments is always a good idea? Commit to your answer.
Concept: Explore advanced considerations like performance, memory, and event argument reuse.
Passing large objects or collections in event arguments can hurt performance and cause memory issues. Sometimes, passing references or IDs is better. Also, avoid reusing event argument instances across events to prevent confusing data. Experts design event args carefully balancing detail and efficiency.
Result
You write efficient, safe event code that scales well in real applications.
Knowing the tradeoffs in event argument design helps avoid subtle bugs and performance problems.
Under the Hood
When an event is raised, the .NET runtime calls all subscribed event handlers in order. The event arguments object is created before raising the event and passed as a parameter. Custom event arguments are just normal objects inheriting from EventArgs, so they carry data like any other object. The event handlers receive this object reference and can read its properties. This mechanism allows flexible data passing without changing the event system itself.
Why designed this way?
The event system in .NET was designed to be simple and extensible. Using a base EventArgs class allows any custom data to be passed without changing the event delegate types. The generic EventHandler delegate was introduced later to avoid creating many delegate types. This design balances type safety, flexibility, and backward compatibility.
┌───────────────┐
│ Event Raised  │
└──────┬────────┘
       │ creates
       ▼
┌────────────────────┐
│ Custom EventArgs    │
│ (data properties)  │
└──────┬─────────────┘
       │ passed to
       ▼
┌───────────────┐
│ Event Handler │
│ (reads data)  │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do you think you must create a new delegate type for every custom event argument class? Commit to yes or no.
Common Belief:You must create a new delegate type for each custom event argument class to handle events properly.
Tap to reveal reality
Reality:You can use the generic EventHandler delegate with your custom event argument class, avoiding the need for new delegate types.
Why it matters:Creating unnecessary delegate types clutters code and makes maintenance harder.
Quick: Do you think event handlers receive copies of event arguments or references? Commit to your answer.
Common Belief:Event handlers receive copies of event arguments, so changing them inside handlers won't affect others.
Tap to reveal reality
Reality:Event handlers receive references to the same event argument object, so changes affect all handlers.
Why it matters:Mutating event arguments inside handlers can cause unexpected side effects and bugs.
Quick: Do you think it's okay to reuse the same event argument instance for multiple events? Commit to yes or no.
Common Belief:Reusing the same event argument instance for multiple events is efficient and safe.
Tap to reveal reality
Reality:Reusing event argument instances can cause data confusion and bugs because handlers may see stale or wrong data.
Why it matters:Reusing instances breaks the assumption that event data is unique per event, leading to hard-to-find errors.
Quick: Do you think passing large data objects in event arguments is always a good idea? Commit to yes or no.
Common Belief:Passing large data objects in event arguments is fine and has no downside.
Tap to reveal reality
Reality:Passing large data can hurt performance and increase memory use; sometimes passing references or summaries is better.
Why it matters:Ignoring performance impacts can make applications slow or crash under heavy event loads.
Expert Zone
1
Custom event arguments should be immutable to prevent accidental changes after the event is raised, which can cause subtle bugs.
2
Using EventHandler avoids delegate proliferation but requires understanding generic delegates and matching signatures.
3
Passing minimal necessary data in event arguments improves performance and reduces memory pressure in high-frequency events.
When NOT to use
Avoid custom event arguments when the event does not need to send extra data; use simple EventArgs instead. For very complex event data or asynchronous event handling, consider using specialized event systems or messaging frameworks like Reactive Extensions or event buses.
Production Patterns
In production, custom event arguments are used to pass context like user info, error details, or state changes. Patterns include immutable event args, using EventHandler, and careful design to avoid large payloads. Logging and telemetry systems often rely on custom event arguments to capture rich event data.
Connections
Observer pattern
Custom event arguments build on the observer pattern by adding detailed data to notifications.
Understanding custom event arguments deepens your grasp of how observers communicate rich information, not just signals.
Immutable objects
Custom event arguments are often designed as immutable objects to ensure data consistency.
Knowing immutability principles helps you design safer event data that cannot be changed unexpectedly.
Messaging systems in distributed computing
Custom event arguments are like message payloads in distributed systems, carrying data between components.
Seeing event arguments as messages helps understand data flow and decoupling in large software architectures.
Common Pitfalls
#1Mutating event argument properties inside event handlers.
Wrong approach:void OnTemperatureChanged(object sender, TemperatureChangedEventArgs e) { e.NewTemperature = 100; // Wrong: changing event data }
Correct approach:void OnTemperatureChanged(object sender, TemperatureChangedEventArgs e) { var temp = e.NewTemperature; // Read-only usage }
Root cause:Misunderstanding that event arguments should be immutable and treated as read-only data.
#2Creating a new delegate type unnecessarily for custom event arguments.
Wrong approach:public delegate void TemperatureChangedHandler(object sender, TemperatureChangedEventArgs e); public event TemperatureChangedHandler TemperatureChanged;
Correct approach:public event EventHandler TemperatureChanged;
Root cause:Not knowing about the generic EventHandler delegate provided by .NET.
#3Reusing the same event argument instance for multiple event raises.
Wrong approach:var args = new TemperatureChangedEventArgs(25); TemperatureChanged?.Invoke(this, args); args = new TemperatureChangedEventArgs(30); TemperatureChanged?.Invoke(this, args); // Reusing variable but creating new instances is okay // But reusing the same instance and changing property would be wrong.
Correct approach:TemperatureChanged?.Invoke(this, new TemperatureChangedEventArgs(25)); TemperatureChanged?.Invoke(this, new TemperatureChangedEventArgs(30));
Root cause:Confusing variable reuse with object reuse; event args should be new instances per event.
Key Takeaways
Custom event arguments let you send detailed information with events, making your program more responsive and informative.
They are classes that inherit from EventArgs and carry properties with event data.
Use the generic EventHandler delegate to simplify event declarations with custom arguments.
Design event argument classes to be immutable to avoid bugs caused by changing data after event raising.
Avoid passing large data in event arguments to keep your program efficient and responsive.

Practice

(1/5)
1. What is the main purpose of creating a custom class that inherits from EventArgs in C#?
easy
A. To replace the EventHandler delegate entirely.
B. To prevent events from being raised multiple times.
C. To send additional data with an event when it is raised.
D. To automatically handle exceptions in event handlers.

Solution

  1. Step 1: Understand the role of EventArgs

    The EventArgs class is used to pass data with events. Creating a custom class inheriting from it allows adding extra information.
  2. Step 2: Identify the purpose of custom event arguments

    Custom event argument classes let you send specific data related to the event, making event communication clearer.
  3. Final Answer:

    To send additional data with an event when it is raised. -> Option C
  4. Quick Check:

    Custom EventArgs = Extra event data [OK]
Hint: Custom EventArgs carry extra info with events [OK]
Common Mistakes:
  • Thinking custom EventArgs replace EventHandler delegate
  • Believing custom EventArgs stop event repetition
  • Assuming custom EventArgs handle exceptions automatically
2. Which of the following is the correct way to declare a custom event argument class named MyEventArgs with a string property Message?
easy
A. public class MyEventArgs : EventArgs { public int Message { get; set; } }
B. public class MyEventArgs { public string Message { get; set; } }
C. public class MyEventArgs : EventHandler { public string Message; }
D. public class MyEventArgs : EventArgs { public string Message { get; set; } }

Solution

  1. Step 1: Check inheritance from EventArgs

    The custom event argument class must inherit from EventArgs to be used properly in events.
  2. Step 2: Verify property type and syntax

    The property Message should be a string with get and set accessors for data storage.
  3. Final Answer:

    public class MyEventArgs : EventArgs { public string Message { get; set; } } -> Option D
  4. Quick Check:

    Custom EventArgs inherit EventArgs + correct property type [OK]
Hint: Custom EventArgs must inherit EventArgs and use correct property types [OK]
Common Mistakes:
  • Forgetting to inherit from EventArgs
  • Using EventHandler as base class instead of EventArgs
  • Declaring property with wrong type
3. Consider the following code snippet:
public class MyEventArgs : EventArgs { public int Number { get; set; } }

public class Publisher {
    public event EventHandler<MyEventArgs> OnNumberChanged;
    public void ChangeNumber(int num) {
        OnNumberChanged?.Invoke(this, new MyEventArgs { Number = num });
    }
}

public class Subscriber {
    public void Subscribe(Publisher pub) {
        pub.OnNumberChanged += HandleNumberChanged;
    }
    private void HandleNumberChanged(object sender, MyEventArgs e) {
        Console.WriteLine($"Number is {e.Number}");
    }
}

// Main method
var pub = new Publisher();
var sub = new Subscriber();
sub.Subscribe(pub);
pub.ChangeNumber(42);

What will be the output when this code runs?
medium
A. Number is 42
B. Number is 0
C. No output, event not raised
D. Compilation error due to event handler signature

Solution

  1. Step 1: Understand event raising and subscription

    The Publisher raises OnNumberChanged event with MyEventArgs containing the number passed.
  2. Step 2: Check subscriber's event handler output

    The subscriber prints the Number property from the event args, which is set to 42.
  3. Final Answer:

    Number is 42 -> Option A
  4. Quick Check:

    Event raised with Number=42 prints "Number is 42" [OK]
Hint: Event args data prints exactly as passed in event [OK]
Common Mistakes:
  • Assuming event is not raised or subscribed
  • Confusing default values with passed values
  • Mistaking event handler signature causing errors
4. The following code attempts to raise a custom event with arguments but causes a runtime error. What is the most likely cause?
public class MyEventArgs : EventArgs { public string Info { get; set; } }

public class Publisher {
    public event EventHandler<MyEventArgs> OnInfoChanged;
    public void RaiseEvent() {
        OnInfoChanged(this, new MyEventArgs { Info = "Hello" });
    }
}

// Usage
var pub = new Publisher();
pub.RaiseEvent();
medium
A. OnInfoChanged event is null because no subscribers are attached.
B. MyEventArgs does not inherit from EventArgs.
C. Event handler signature does not match the event declaration.
D. The Info property is not initialized before raising the event.

Solution

  1. Step 1: Check event invocation safety

    The event OnInfoChanged is invoked without checking if it has subscribers, so if null, it causes a runtime NullReferenceException.
  2. Step 2: Identify missing subscriber check

    Before invoking, the code should check if OnInfoChanged is not null to avoid errors.
  3. Final Answer:

    OnInfoChanged event is null because no subscribers are attached. -> Option A
  4. Quick Check:

    Invoke event without subscribers = NullReferenceException [OK]
Hint: Always check event is not null before invoking [OK]
Common Mistakes:
  • Forgetting to check event for null before invoking
  • Assuming event has subscribers by default
  • Confusing property initialization with event subscription
5. You want to create a custom event argument class ProgressEventArgs that includes a percentage (int) and a message (string). You also want to raise an event ProgressChanged in a class Downloader that sends this data. Which of the following code snippets correctly implements this scenario?
hard
A. public class ProgressEventArgs { public int Percentage { get; set; } public string Message { get; set; } } public class Downloader { public event EventHandler ProgressChanged; public void ReportProgress(int percent, string msg) { ProgressChanged?.Invoke(this, new ProgressEventArgs { Percentage = percent, Message = msg }); } }
B. public class ProgressEventArgs : EventArgs { public int Percentage { get; } public string Message { get; } public ProgressEventArgs(int percentage, string message) { Percentage = percentage; Message = message; } } public class Downloader { public event EventHandler<ProgressEventArgs> ProgressChanged; public void ReportProgress(int percent, string msg) { ProgressChanged?.Invoke(this, new ProgressEventArgs(percent, msg)); } }
C. public class ProgressEventArgs : EventArgs { public int Percentage; public string Message; } public class Downloader { public event EventHandler<ProgressEventArgs> ProgressChanged; public void ReportProgress(int percent, string msg) { ProgressChanged(this, new ProgressEventArgs { Percentage = percent, Message = msg }); } }
D. public class ProgressEventArgs : EventArgs { public int Percentage { get; set; } public string Message { get; set; } } public class Downloader { public event EventHandler<ProgressEventArgs> ProgressChanged; public void ReportProgress(int percent, string msg) { ProgressChanged(this, new ProgressEventArgs { Percentage = percent, Message = msg }); } }

Solution

  1. Step 1: Define immutable custom event args with constructor

    public class ProgressEventArgs : EventArgs { public int Percentage { get; } public string Message { get; } public ProgressEventArgs(int percentage, string message) { Percentage = percentage; Message = message; } } public class Downloader { public event EventHandler<ProgressEventArgs> ProgressChanged; public void ReportProgress(int percent, string msg) { ProgressChanged?.Invoke(this, new ProgressEventArgs(percent, msg)); } } defines ProgressEventArgs inheriting from EventArgs with readonly properties set via constructor, which is a good practice.
  2. Step 2: Raise event safely with null check

    public class Downloader { public event EventHandler<ProgressEventArgs> ProgressChanged; public void ReportProgress(int percent, string msg) { ProgressChanged?.Invoke(this, new ProgressEventArgs(percent, msg)); } } uses null-conditional operator ?. to safely raise the event with the new event args instance.
  3. Final Answer:

    public class ProgressEventArgs : EventArgs { public int Percentage { get; } public string Message { get; } public ProgressEventArgs(int percentage, string message) { Percentage = percentage; Message = message; } } public class Downloader { public event EventHandler<ProgressEventArgs> ProgressChanged; public void ReportProgress(int percent, string msg) { ProgressChanged?.Invoke(this, new ProgressEventArgs(percent, msg)); } } -> Option B
  4. Quick Check:

    Immutable EventArgs via ctor + safe ?.Invoke [OK]
Hint: Use constructor for EventArgs properties and null-check event before invoke [OK]
Common Mistakes:
  • Not inheriting from EventArgs
  • Using wrong event handler delegate type
  • Invoking event without null check
  • Using public fields instead of properties
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard