So implementieren Sie einen Observer

Das Entwurfsmuster des Beobachters erfordert eine Trennung zwischen einem Beobachter, der sich für Benachrichtigungen registriert, und einem Anbieter, der Daten überwacht und Benachrichtigungen an einen oder mehrere Beobachter sendet. In diesem Artikel wird erläutert, wie Sie einen Beobachter erstellen. In einem verwandten Artikel, Wie Sie einen Anbieter implementieren, wird beschrieben, wie man einen Anbieter erstellt.

Erstellen eines Beobachters

Um einen Beobachter zu erstellen, implementieren Sie die System.IObserver<T> Schnittstelle. In den folgenden Schritten werden die einzelnen Member beschrieben, die Sie definieren müssen.

  1. Definieren Sie den Beobachtertyp, der die System.IObserver<T> Schnittstelle implementiert.

    Der folgende Code definiert einen Typ namens TemperatureReporter einer konstruierten System.IObserver<T> Implementierung mit einem generischen Typargument von Temperature.

    namespace TemperatureSample;
    
    public sealed class TemperatureReporter : IObserver<Temperature>
    
    Namespace Global.TemperatureSample
    
        Public NotInheritable Class TemperatureReporter
            Implements IObserver(Of Temperature)
    
  2. Wenn der Beobachter das Abonnement beenden muss, bevor der Anbieter IObserver<T>.OnCompleted aufruft, definieren Sie eine private Variable, um den von IObservable<T>.Subscribe zurückgegebenen IDisposable zu speichern, und definieren Sie eine Methode zum Abonnieren.

    Die private Variable unsubscriber speichert das IDisposable Objekt. Die Subscribe Methode ruft die Methode des Anbieters Subscribe auf und weist das zurückgegebene Objekt zu unsubscriber.

    namespace TemperatureSample;
    
    public sealed class TemperatureReporter : IObserver<Temperature>
    {
        private IDisposable? _unsubscriber;
        private Temperature? _last;
    
        public void Subscribe(IObservable<Temperature> provider)
        {
            ArgumentNullException.ThrowIfNull(provider);
            _unsubscriber = provider.Subscribe(this);
        }
    
    Namespace Global.TemperatureSample
    
        Public NotInheritable Class TemperatureReporter
            Implements IObserver(Of Temperature)
    
            Private _unsubscriber As IDisposable
            Private _last As Temperature?
    
            Public Sub Subscribe(provider As IObservable(Of Temperature))
                ArgumentNullException.ThrowIfNull(provider)
                _unsubscriber = provider.Subscribe(Me)
            End Sub
    
  3. Definieren Sie eine Unsubscribe Methode, mit der der Beobachter nicht mehr Benachrichtigungen empfängt, bevor der Anbieter aufruft IObserver<T>.OnCompleted.

    public void Unsubscribe() => _unsubscriber?.Dispose();
    
    Public Sub Unsubscribe()
        _unsubscriber?.Dispose()
    End Sub
    
  4. Implementieren Sie die drei Methoden, die von IObserver<T> definiert werden: IObserver<T>.OnNext, IObserver<T>.OnError und IObserver<T>.OnCompleted.

    Die Methoden OnError und OnCompleted können Stub-Implementierungen sein. Die Methode OnError sollte das übergebene Exception nicht als Ausnahme behandeln, und OnCompleted kann die IDisposable.Dispose-Implementierung des Anbieters aufrufen.

    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");
    
    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }
    
    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");
    
        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }
    
        _last = value;
    }
    
    Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
        Console.WriteLine("Additional temperature data won't be transmitted.")
    End Sub
    
    ' OnError is informational; observers shouldn't treat it as an exception to handle.
    Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
    End Sub
    
    Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")
    
        If _last.HasValue Then
            Dim previous = _last.Value
            Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
        End If
    
        _last = value
    End Sub
    

Vollständiges Beispiel

Das folgende Beispiel zeigt den vollständigen Quellcode für die TemperatureReporter Klasse, der die IObserver<T> Implementierung für eine Temperaturüberwachungsanwendung bereitstellt.

namespace TemperatureSample;

public sealed class TemperatureReporter : IObserver<Temperature>
{
    private IDisposable? _unsubscriber;
    private Temperature? _last;

    public void Subscribe(IObservable<Temperature> provider)
    {
        ArgumentNullException.ThrowIfNull(provider);
        _unsubscriber = provider.Subscribe(this);
    }

    public void Unsubscribe() => _unsubscriber?.Dispose();

    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");

    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }

    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");

        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }

        _last = value;
    }
}
Namespace Global.TemperatureSample

    Public NotInheritable Class TemperatureReporter
        Implements IObserver(Of Temperature)

        Private _unsubscriber As IDisposable
        Private _last As Temperature?

        Public Sub Subscribe(provider As IObservable(Of Temperature))
            ArgumentNullException.ThrowIfNull(provider)
            _unsubscriber = provider.Subscribe(Me)
        End Sub

        Public Sub Unsubscribe()
            _unsubscriber?.Dispose()
        End Sub

        Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
            Console.WriteLine("Additional temperature data won't be transmitted.")
        End Sub

        ' OnError is informational; observers shouldn't treat it as an exception to handle.
        Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
        End Sub

        Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
            Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")

            If _last.HasValue Then
                Dim previous = _last.Value
                Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
                Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
            End If

            _last = value
        End Sub
    End Class

End Namespace