EventArgs and event data
EventArgs is the base class for data passed with an event. It is empty by default. It gives every handler the same shape, void Handler(object sender, EventArgs e), where sender raised the event and e carries the data (see EventHandlerDelegatesA type-safe function pointer, so a method can be passed around like any other value.Opens in a new tab).
No data
EventArgs.Empty is a shared instance, which avoids allocating an empty object each time.
public event EventHandler Clicked; public void Click(){ Clicked?.Invoke(this, EventArgs.Empty);}With data
A class derives from EventArgs, and the event uses EventHandler<T>.
class MessageEventArgs : EventArgs{ public string Message { get; } public MessageEventArgs(string message) => Message = message;} class Chat{ public event EventHandler<MessageEventArgs> MessageReceived; public void Receive(string msg) { MessageReceived?.Invoke(this, new MessageEventArgs(msg)); }}Deriving from EventArgs is a convention. Since .NET Framework 4.5, EventHandler<TEventArgs> no longer requires it.
Built-in types
PropertyChangedEventArgs names the property that changed. CancelEventArgs has a Cancel flag to veto an action. ElapsedEventArgs comes from System.Timers.Timer. DataReceivedEventArgs comes from Process.
form.Closing += (sender, e) =>{ if (unsavedChanges) e.Cancel = true; // stop the close from happening};Where it matters
.NET has no generic EventArgs<T>. A small custom class says exactly what the event carries and is usually clearer than a reusable wrapper.