Udostępnij za pośrednictwem


Wywoływanie zdarzeń i korzystanie z nich

W tym artykule pokazano, jak pracować ze zdarzeniami w .NET za pomocą delegata EventHandler, delegata EventHandler<TEventArgs> oraz delegata niestandardowego, wraz z przykładami zdarzeń z danymi i bez danych.

Wymagania wstępne

Zapoznaj się z pojęciami w artykule Zdarzenia .

Zgłaszanie zdarzenia bez danych

Te kroki tworzą klasę Counter , która uruchamia ThresholdReached zdarzenie, gdy suma bieżąca osiągnie lub przekroczy próg.

  1. Zadeklaruj zdarzenie przy użyciu delegata EventHandler .

    Użyj EventHandler, gdy zdarzenie nie przekazuje danych do obsługi:

    public event EventHandler? ThresholdReached;
    
    Public Event ThresholdReached As EventHandler
    
  2. Dodaj metodę protected virtual (Protected Overridable w Visual Basic), aby zgłosić zdarzenie.

    Ten wzorzec pozwala klasom pochodnym zastąpić zachowanie podnoszenia zdarzeń bez bezpośredniego wywoływania delegata. W języku C#użyj operatora warunkowego o wartości null (?.), aby chronić się przed żadnymi subskrybentami (w Visual Basic RaiseEvent obsługuje to automatycznie):

    protected virtual void OnThresholdReached(EventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }
    
    Protected Overridable Sub OnThresholdReached(e As EventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub
    
  3. Wywołaj metodę "raise" po spełnieniu warunku.

    Przekaż Empty, ponieważ to zdarzenie nie niesie żadnych danych.

    if (_total >= _threshold)
    {
        OnThresholdReached(EventArgs.Empty);
    }
    
    If (_total >= _threshold) Then
        OnThresholdReached(EventArgs.Empty)
    End If
    
  4. Subskrybowanie zdarzenia przy użyciu += operatora (w Visual Basic, AddHandler):

    c.ThresholdReached += c_ThresholdReached;
    
    AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
    
  5. Zdefiniuj metodę obsługi zdarzeń.

    Jego podpis musi być zgodny z delegatem EventHandler — pierwszy parametr to źródło zdarzenia, a drugi to 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
    

W poniższym przykładzie przedstawiono kompletną implementację:

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

Zgłaszanie zdarzenia przy użyciu danych

Te kroki rozszerzają poprzedni Counter przykład, aby wywołać zdarzenie zawierające dane — wartość progową oraz czas, w którym została osiągnięta.

  1. Zdefiniuj klasę danych zdarzeń dziedziczącą z EventArgs klasy.

    Dodaj właściwości dla każdego elementu danych, które chcesz przekazać do programu obsługi:

    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. Zadeklaruj zdarzenie przy użyciu delegata EventHandler<TEventArgs> , przekazując klasę danych zdarzenia jako argument typu:

    public event EventHandler<ThresholdReachedEventArgs>? ThresholdReached;
    
    Public Event ThresholdReached As EventHandler(Of ThresholdReachedEventArgs)
    
  3. Dodaj metodę protected virtual (Protected Overridable w Visual Basic), aby zgłosić zdarzenie.

    Ten wzorzec pozwala klasom pochodnym zastąpić zachowanie podnoszenia zdarzeń bez bezpośredniego wywoływania delegata. W języku C#użyj operatora warunkowego o wartości null (?.), aby chronić się przed żadnymi subskrybentami (w Visual Basic RaiseEvent obsługuje to automatycznie):

    protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }
    
    Protected Overridable Sub OnThresholdReached(e As ThresholdReachedEventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub
    
  4. Wypełnij obiekt danych zdarzenia i wywołaj metodę raise po spełnieniu warunku:

    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. Subskrybowanie zdarzenia przy użyciu += operatora (w Visual Basic, AddHandler):

    c.ThresholdReached += c_ThresholdReached;
    
    AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
    
  6. Zdefiniuj procedurę obsługi zdarzeń.

    Drugi typ parametru to ThresholdReachedEventArgs zamiast EventArgs, co umożliwia programowi obsługi odczytywanie danych zdarzenia.

    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
    

W poniższym przykładzie przedstawiono kompletną implementację:

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

Zadeklaruj niestandardowego delegata dla zdarzenia

Delegata niestandardowego deklaruj tylko w rzadkich przypadkach, na przykład gdy chcesz udostępnić swoją klasę starszemu kodowi, który nie może używać typów ogólnych. W większości przypadków użyj polecenia EventHandler<TEventArgs> , jak pokazano w poprzedniej sekcji.

  1. Zadeklaruj niestandardowy typ delegata.

    Sygnatura delegata musi być zgodna z podpisem procedury obsługi zdarzeń — dwoma parametrami: źródłem zdarzeń (object; w Visual Basic Object) i klasą danych zdarzenia:

    public delegate void ThresholdReachedEventHandler(object sender, ThresholdReachedEventArgs e);
    
    Delegate Sub ThresholdReachedEventHandler(sender As Object, e As ThresholdReachedEventArgs)
    
  2. Zadeklaruj zdarzenie przy użyciu niestandardowego typu delegata zamiast EventHandler<TEventArgs>:

    public event ThresholdReachedEventHandler? ThresholdReached;
    
    Public Event ThresholdReached As ThresholdReachedEventHandler
    
  3. Dodaj metodę protected virtual (Protected Overridable w Visual Basic), aby zgłosić zdarzenie.

    W języku C#użyj operatora warunkowego o wartości null (?.), aby chronić się przed żadnymi subskrybentami (w Visual Basic RaiseEvent obsługuje to automatycznie):

    protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }
    
    Protected Overridable Sub OnThresholdReached(e As ThresholdReachedEventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub
    
  4. Wypełnij obiekt danych zdarzenia i wywołaj metodę raise po spełnieniu warunku:

    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. Subskrybowanie zdarzenia przy użyciu += operatora (w Visual Basic, AddHandler):

    c.ThresholdReached += c_ThresholdReached;
    
    AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
    
  6. Zdefiniuj procedurę obsługi zdarzeń.

    Sygnatura programu obsługi musi być zgodna z niestandardowym delegatem —object dla nadawcy i klasy danych zdarzenia dla drugiego parametru:

    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
    

W poniższym przykładzie przedstawiono kompletną implementację:

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)