Edit

Share via


Raise and consume events

This article shows how to work with events in .NET using the EventHandler delegate, the EventHandler<TEventArgs> delegate, and a custom delegate, with examples for events with and without data.

Prerequisites

Familiarize yourself with the concepts in the Events article.

Raise an event without data

These steps create a Counter class that fires a ThresholdReached event when a running total reaches or exceeds a threshold.

  1. Declare the event using the EventHandler delegate.

    Use EventHandler when your event doesn't pass data to the handler:

    public event EventHandler? ThresholdReached;
    
    Public Event ThresholdReached As EventHandler
    
  2. Add a protected virtual method (Protected Overridable in Visual Basic) to raise the event.

    This pattern lets derived classes override the event-raising behavior without directly invoking the delegate. In C#, use the null-conditional operator (?.) to guard against no subscribers (in Visual Basic, RaiseEvent handles this automatically):

    protected virtual void OnThresholdReached(EventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }
    
    Protected Overridable Sub OnThresholdReached(e As EventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub
    
  3. Call the raise method when the condition is met.

    Pass Empty because this event carries no data:

    if (_total >= _threshold)
    {
        OnThresholdReached(EventArgs.Empty);
    }
    
    If (_total >= _threshold) Then
        OnThresholdReached(EventArgs.Empty)
    End If
    
  4. Subscribe to the event using the += operator (in Visual Basic, AddHandler):

    c.ThresholdReached += c_ThresholdReached;
    
    AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
    
  5. Define the event handler method.

    Its signature must match the EventHandler delegate—the first parameter is the event source and the second is EventArgs:

    static void c_ThresholdReached(object? sender, EventArgs e)
    {
        Console.WriteLine("The threshold was reached.");
        Environment.Exit(0);
    }
    
    Sub c_ThresholdReached(sender As Object, e As EventArgs)
        Console.WriteLine("The threshold was reached.")
        Environment.Exit(0)
    End Sub
    

The following example shows the complete implementation:

class EventNoData
{
    static void Main()
    {
        Counter c = new(new Random().Next(10));
        c.ThresholdReached += c_ThresholdReached;

        Console.WriteLine("press 'a' key to increase total");
        while (Console.ReadKey(true).KeyChar == 'a')
        {
            Console.WriteLine("adding one");
            c.Add(1);
        }
    }

    static void c_ThresholdReached(object? sender, EventArgs e)
    {
        Console.WriteLine("The threshold was reached.");
        Environment.Exit(0);
    }
}

class Counter(int passedThreshold)
{
    private readonly int _threshold = passedThreshold;
    private int _total;

    public void Add(int x)
    {
        _total += x;
        if (_total >= _threshold)
        {
            OnThresholdReached(EventArgs.Empty);
        }
    }

    protected virtual void OnThresholdReached(EventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }

    public event EventHandler? ThresholdReached;
}
Module EventNoData

    Sub Main()
        Dim c As New Counter(New Random().Next(10))
        AddHandler c.ThresholdReached, AddressOf c_ThresholdReached

        Console.WriteLine("press 'a' key to increase total")
        While Console.ReadKey(True).KeyChar = "a"
            Console.WriteLine("adding one")
            c.Add(1)
        End While
    End Sub

    Sub c_ThresholdReached(sender As Object, e As EventArgs)
        Console.WriteLine("The threshold was reached.")
        Environment.Exit(0)
    End Sub
End Module

Class Counter
    Private ReadOnly _threshold As Integer
    Private _total As Integer

    Public Sub New(passedThreshold As Integer)
        _threshold = passedThreshold
    End Sub

    Public Sub Add(x As Integer)
        _total += x
        If (_total >= _threshold) Then
            OnThresholdReached(EventArgs.Empty)
        End If
    End Sub

    Protected Overridable Sub OnThresholdReached(e As EventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub

    Public Event ThresholdReached As EventHandler
End Class

Raise an event with data

These steps extend the previous Counter example to raise an event that includes data—the threshold value and the time it was reached.

  1. Define an event data class that inherits from EventArgs.

    Add properties for each piece of data you want to pass to the handler:

    public class ThresholdReachedEventArgs : EventArgs
    {
        public int Threshold { get; set; }
        public DateTime TimeReached { get; set; }
    }
    
    Class ThresholdReachedEventArgs
        Inherits EventArgs
    
        Public Property Threshold As Integer
        Public Property TimeReached As Date
    End Class
    
  2. Declare the event using the EventHandler<TEventArgs> delegate, passing your event data class as the type argument:

    public event EventHandler<ThresholdReachedEventArgs>? ThresholdReached;
    
    Public Event ThresholdReached As EventHandler(Of ThresholdReachedEventArgs)
    
  3. Add a protected virtual method (Protected Overridable in Visual Basic) to raise the event.

    This pattern lets derived classes override the event-raising behavior without directly invoking the delegate. In C#, use the null-conditional operator (?.) to guard against no subscribers (in Visual Basic, RaiseEvent handles this automatically):

    protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }
    
    Protected Overridable Sub OnThresholdReached(e As ThresholdReachedEventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub
    
  4. Populate the event data object and call the raise method when the condition is met:

    if (_total >= _threshold)
    {
        ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
        args.Threshold = _threshold;
        args.TimeReached = DateTime.Now;
        OnThresholdReached(args);
    }
    
    If (_total >= _threshold) Then
        Dim args As New ThresholdReachedEventArgs With {
            .Threshold = _threshold,
            .TimeReached = Date.Now
        }
        OnThresholdReached(args)
    End If
    
  5. Subscribe to the event using the += operator (in Visual Basic, AddHandler):

    c.ThresholdReached += c_ThresholdReached;
    
    AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
    
  6. Define the event handler.

    The second parameter type is ThresholdReachedEventArgs instead of EventArgs, which lets the handler read the event data:

    static void c_ThresholdReached(object? sender, ThresholdReachedEventArgs e)
    {
        Console.WriteLine($"The threshold of {e.Threshold} was reached at {e.TimeReached}.");
        Environment.Exit(0);
    }
    
    Sub c_ThresholdReached(sender As Object, e As ThresholdReachedEventArgs)
        Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached)
        Environment.Exit(0)
    End Sub
    

The following example shows the complete implementation:

class EventWithData
{
    static void Main()
    {
        CounterWithData c = new(new Random().Next(10));
        c.ThresholdReached += c_ThresholdReached;

        Console.WriteLine("press 'a' key to increase total");
        while (Console.ReadKey(true).KeyChar == 'a')
        {
            Console.WriteLine("adding one");
            c.Add(1);
        }
    }

    static void c_ThresholdReached(object? sender, ThresholdReachedEventArgs e)
    {
        Console.WriteLine($"The threshold of {e.Threshold} was reached at {e.TimeReached}.");
        Environment.Exit(0);
    }
}

class CounterWithData(int passedThreshold)
{
    private readonly int _threshold = passedThreshold;
    private int _total;

    public void Add(int x)
    {
        _total += x;
        if (_total >= _threshold)
        {
            ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
            args.Threshold = _threshold;
            args.TimeReached = DateTime.Now;
            OnThresholdReached(args);
        }
    }

    protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }

    public event EventHandler<ThresholdReachedEventArgs>? ThresholdReached;
}

public class ThresholdReachedEventArgs : EventArgs
{
    public int Threshold { get; set; }
    public DateTime TimeReached { get; set; }
}
Module EventWithData

    Sub Main()
        Dim c As New CounterWithData(New Random().Next(10))
        AddHandler c.ThresholdReached, AddressOf c_ThresholdReached

        Console.WriteLine("press 'a' key to increase total")
        While Console.ReadKey(True).KeyChar = "a"
            Console.WriteLine("adding one")
            c.Add(1)
        End While
    End Sub

    Sub c_ThresholdReached(sender As Object, e As ThresholdReachedEventArgs)
        Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached)
        Environment.Exit(0)
    End Sub
End Module

Class CounterWithData
    Private ReadOnly _threshold As Integer
    Private _total As Integer

    Public Sub New(passedThreshold As Integer)
        _threshold = passedThreshold
    End Sub

    Public Sub Add(x As Integer)
        _total += x
        If (_total >= _threshold) Then
            Dim args As New ThresholdReachedEventArgs With {
                .Threshold = _threshold,
                .TimeReached = Date.Now
            }
            OnThresholdReached(args)
        End If
    End Sub

    Protected Overridable Sub OnThresholdReached(e As ThresholdReachedEventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub

    Public Event ThresholdReached As EventHandler(Of ThresholdReachedEventArgs)
End Class

Class ThresholdReachedEventArgs
    Inherits EventArgs

    Public Property Threshold As Integer
    Public Property TimeReached As Date
End Class

Declare a custom delegate for an event

Declare a custom delegate only in rare scenarios, such as making your class available to legacy code that can't use generics. For most cases, use EventHandler<TEventArgs> as shown in the previous section.

  1. Declare the custom delegate type.

    The delegate signature must match the event handler signature—two parameters: the event source (object; in Visual Basic, Object) and the event data class:

    public delegate void ThresholdReachedEventHandler(object sender, ThresholdReachedEventArgs e);
    
    Delegate Sub ThresholdReachedEventHandler(sender As Object, e As ThresholdReachedEventArgs)
    
  2. Declare the event using your custom delegate type instead of EventHandler<TEventArgs>:

    public event ThresholdReachedEventHandler? ThresholdReached;
    
    Public Event ThresholdReached As ThresholdReachedEventHandler
    
  3. Add a protected virtual method (Protected Overridable in Visual Basic) to raise the event.

    In C#, use the null-conditional operator (?.) to guard against no subscribers (in Visual Basic, RaiseEvent handles this automatically):

    protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }
    
    Protected Overridable Sub OnThresholdReached(e As ThresholdReachedEventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub
    
  4. Populate the event data object and call the raise method when the condition is met:

    if (_total >= _threshold)
    {
        ThresholdReachedEventArgs args = new();
        args.Threshold = _threshold;
        args.TimeReached = DateTime.Now;
        OnThresholdReached(args);
    }
    
    If (_total >= _threshold) Then
        Dim args As New ThresholdReachedEventArgs With {
            .Threshold = _threshold,
            .TimeReached = Date.Now
        }
        OnThresholdReached(args)
    End If
    
  5. Subscribe to the event using the += operator (in Visual Basic, AddHandler):

    c.ThresholdReached += c_ThresholdReached;
    
    AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
    
  6. Define the event handler.

    The handler signature must match the custom delegate—object for the sender and your event data class for the second parameter:

    static void c_ThresholdReached(object sender, ThresholdReachedEventArgs e)
    {
        Console.WriteLine($"The threshold of {e.Threshold} was reached at {e.TimeReached}.");
        Environment.Exit(0);
    }
    
    Sub c_ThresholdReached(sender As Object, e As ThresholdReachedEventArgs)
        Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached)
        Environment.Exit(0)
    End Sub
    

The following example shows the complete implementation:

class EventWithDelegate
{
    static void Main()
    {
        CounterWithDelegate c = new(new Random().Next(10));
        c.ThresholdReached += c_ThresholdReached;

        Console.WriteLine("press 'a' key to increase total");
        while (Console.ReadKey(true).KeyChar == 'a')
        {
            Console.WriteLine("adding one");
            c.Add(1);
        }
    }

    static void c_ThresholdReached(object sender, ThresholdReachedEventArgs e)
    {
        Console.WriteLine($"The threshold of {e.Threshold} was reached at {e.TimeReached}.");
        Environment.Exit(0);
    }
}

class CounterWithDelegate(int passedThreshold)
{
    private readonly int _threshold = passedThreshold;
    private int _total;

    public void Add(int x)
    {
        _total += x;
        if (_total >= _threshold)
        {
            ThresholdReachedEventArgs args = new();
            args.Threshold = _threshold;
            args.TimeReached = DateTime.Now;
            OnThresholdReached(args);
        }
    }

    protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }

    public event ThresholdReachedEventHandler? ThresholdReached;
}

public delegate void ThresholdReachedEventHandler(object sender, ThresholdReachedEventArgs e);
Module EventWithDelegate

    Sub Main()
        Dim c As New CounterWithDelegate(New Random().Next(10))
        AddHandler c.ThresholdReached, AddressOf c_ThresholdReached

        Console.WriteLine("press 'a' key to increase total")
        While Console.ReadKey(True).KeyChar = "a"
            Console.WriteLine("adding one")
            c.Add(1)
        End While
    End Sub

    Sub c_ThresholdReached(sender As Object, e As ThresholdReachedEventArgs)
        Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached)
        Environment.Exit(0)
    End Sub
End Module

Class CounterWithDelegate
    Private ReadOnly _threshold As Integer
    Private _total As Integer

    Public Sub New(passedThreshold As Integer)
        _threshold = passedThreshold
    End Sub

    Public Sub Add(x As Integer)
        _total += x
        If (_total >= _threshold) Then
            Dim args As New ThresholdReachedEventArgs With {
                .Threshold = _threshold,
                .TimeReached = Date.Now
            }
            OnThresholdReached(args)
        End If
    End Sub

    Protected Overridable Sub OnThresholdReached(e As ThresholdReachedEventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub

    Public Event ThresholdReached As ThresholdReachedEventHandler
End Class

Delegate Sub ThresholdReachedEventHandler(sender As Object, e As ThresholdReachedEventArgs)