Procedure: Een provider implementeren
Het ontwerppatroon van de waarnemer vereist een verdeling tussen een provider, die gegevens bewaakt en meldingen verzendt, en een of meer waarnemers, die meldingen (callbacks) van de provider ontvangen. In dit onderwerp wordt beschreven hoe u een provider maakt. Een gerelateerd onderwerp, Procedure: Een waarnemer implementeren, beschrijft hoe u een waarnemer maakt.
Een provider maken
Definieer de gegevens die de provider verantwoordelijk is voor het verzenden naar waarnemers. Hoewel de provider en de gegevens die worden verzonden naar waarnemers één type kunnen zijn, worden ze over het algemeen vertegenwoordigd door verschillende typen. In een toepassing voor temperatuurbewaking definieert de
Temperature
structuur bijvoorbeeld de gegevens die de provider (die wordt vertegenwoordigd door deTemperatureMonitor
klasse die in de volgende stap is gedefinieerd) bewaakt en waarop waarnemers zich abonneren.using System; public struct Temperature { private decimal temp; private DateTime tempDate; public Temperature(decimal temperature, DateTime dateAndTime) { this.temp = temperature; this.tempDate = dateAndTime; } public decimal Degrees { get { return this.temp; } } public DateTime Date { get { return this.tempDate; } } }
Public Structure Temperature Private temp As Decimal Private tempDate As DateTime Public Sub New(ByVal temperature As Decimal, ByVal dateAndTime As DateTime) Me.temp = temperature Me.tempDate = dateAndTime End Sub Public ReadOnly Property Degrees As Decimal Get Return Me.temp End Get End Property Public ReadOnly Property [Date] As DateTime Get Return tempDate End Get End Property End Structure
Definieer de gegevensprovider, een type waarmee de System.IObservable<T> interface wordt geïmplementeerd. Het algemene typeargument van de provider is het type dat de provider naar waarnemers verzendt. In het volgende voorbeeld wordt een
TemperatureMonitor
klasse gedefinieerd. Dit is een geconstrueerde System.IObservable<T> implementatie met een algemeen typeargument vanTemperature
.using System; using System.Collections.Generic; public class TemperatureMonitor : IObservable<Temperature> {
Imports System.Collections.Generic Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
Bepaal hoe de provider verwijzingen naar waarnemers opslaat, zodat elke waarnemer zo nodig op de hoogte kan worden gesteld. Meestal wordt voor dit doel een verzamelingsobject zoals een algemeen List<T> object gebruikt. In het volgende voorbeeld wordt een privéobject List<T> gedefinieerd dat wordt geïnstantieerd in de
TemperatureMonitor
klasseconstructor.using System; using System.Collections.Generic; public class TemperatureMonitor : IObservable<Temperature> { List<IObserver<Temperature>> observers; public TemperatureMonitor() { observers = new List<IObserver<Temperature>>(); }
Imports System.Collections.Generic Public Class TemperatureMonitor : Implements IObservable(Of Temperature) Dim observers As List(Of IObserver(Of Temperature)) Public Sub New() observers = New List(Of IObserver(Of Temperature)) End Sub
Definieer een IDisposable implementatie die de provider kan retourneren aan abonnees, zodat ze op elk gewenst moment geen meldingen meer kunnen ontvangen. In het volgende voorbeeld wordt een geneste
Unsubscriber
klasse gedefinieerd die wordt doorgegeven aan de verzameling abonnees en aan de abonnee wanneer de klasse wordt geïnstantieerd. Met deze code kan de abonnee de implementatie van IDisposable.Dispose het object aanroepen om zichzelf uit de verzameling abonnees te verwijderen.private class Unsubscriber : IDisposable { private List<IObserver<Temperature>> _observers; private IObserver<Temperature> _observer; public Unsubscriber(List<IObserver<Temperature>> observers, IObserver<Temperature> observer) { this._observers = observers; this._observer = observer; } public void Dispose() { if (! (_observer == null)) _observers.Remove(_observer); } }
Private Class Unsubscriber : Implements IDisposable Private _observers As List(Of IObserver(Of Temperature)) Private _observer As IObserver(Of Temperature) Public Sub New(ByVal observers As List(Of IObserver(Of Temperature)), ByVal observer As IObserver(Of Temperature)) Me._observers = observers Me._observer = observer End Sub Public Sub Dispose() Implements IDisposable.Dispose If _observer IsNot Nothing Then _observers.Remove(_observer) End Sub End Class
Implementeer de IObservable<T>.Subscribe methode. De methode wordt doorgegeven aan de System.IObserver<T> interface en moet worden opgeslagen in het object dat voor dat doel is ontworpen in stap 3. De methode moet vervolgens de IDisposable in stap 4 ontwikkelde implementatie retourneren. In het volgende voorbeeld ziet u de implementatie van de Subscribe methode in de
TemperatureMonitor
klasse.public IDisposable Subscribe(IObserver<Temperature> observer) { if (! observers.Contains(observer)) observers.Add(observer); return new Unsubscriber(observers, observer); }
Public Function Subscribe(ByVal observer As System.IObserver(Of Temperature)) As System.IDisposable Implements System.IObservable(Of Temperature).Subscribe If Not observers.Contains(observer) Then observers.Add(observer) End If Return New Unsubscriber(observers, observer) End Function
Stel waarnemers op de hoogte door hun IObserver<T>.OnNext, IObserver<T>.OnErroren IObserver<T>.OnCompleted implementaties aan te roepen. In sommige gevallen kan een provider de OnError methode niet aanroepen wanneer er een fout optreedt. Met de volgende
GetTemperature
methode wordt bijvoorbeeld een monitor gesimuleerd die elke vijf seconden temperatuurgegevens leest en waarnemers op de hoogte stelt als de temperatuur is gewijzigd met ten minste 0,1 graden sinds de vorige lezing. Als het apparaat geen temperatuur rapporteert (dat wil gezegd, als de waarde null is), meldt de provider waarnemers dat de overdracht is voltooid. Houd er rekening mee dat, naast het aanroepen van de methode van OnCompleted elke waarnemer, deGetTemperature
verzameling wordt gewist List<T> . In dit geval roept de provider geen aanroepen uit naar de OnError methode van de waarnemers.public void GetTemperature() { // Create an array of sample data to mimic a temperature device. Nullable<Decimal>[] temps = {14.6m, 14.65m, 14.7m, 14.9m, 14.9m, 15.2m, 15.25m, 15.2m, 15.4m, 15.45m, null }; // Store the previous temperature, so notification is only sent after at least .1 change. Nullable<Decimal> previous = null; bool start = true; foreach (var temp in temps) { System.Threading.Thread.Sleep(2500); if (temp.HasValue) { if (start || (Math.Abs(temp.Value - previous.Value) >= 0.1m )) { Temperature tempData = new Temperature(temp.Value, DateTime.Now); foreach (var observer in observers) observer.OnNext(tempData); previous = temp; if (start) start = false; } } else { foreach (var observer in observers.ToArray()) if (observer != null) observer.OnCompleted(); observers.Clear(); break; } } }
Public Sub GetTemperature() ' Create an array of sample data to mimic a temperature device. Dim temps() As Nullable(Of Decimal) = {14.6D, 14.65D, 14.7D, 14.9D, 14.9D, 15.2D, 15.25D, 15.2D, 15.4D, 15.45D, Nothing} ' Store the previous temperature, so notification is only sent after at least .1 change. Dim previous As Nullable(Of Decimal) Dim start As Boolean = True For Each temp In temps System.Threading.Thread.Sleep(2500) If temp.HasValue Then If start OrElse Math.Abs(temp.Value - previous.Value) >= 0.1 Then Dim tempData As New Temperature(temp.Value, Date.Now) For Each observer In observers observer.OnNext(tempData) Next previous = temp If start Then start = False End If Else For Each observer In observers.ToArray() If observer IsNot Nothing Then observer.OnCompleted() Next observers.Clear() Exit For End If Next End Sub
Opmerking
Het volgende voorbeeld bevat de volledige broncode voor het definiëren van een IObservable<T> implementatie voor een toepassing voor temperatuurbewaking. Het omvat de Temperature
structuur, de gegevens die naar waarnemers worden verzonden en de TemperatureMonitor
klasse, die de IObservable<T> implementatie is.
using System.Threading;
using System;
using System.Collections.Generic;
public class TemperatureMonitor : IObservable<Temperature>
{
List<IObserver<Temperature>> observers;
public TemperatureMonitor()
{
observers = new List<IObserver<Temperature>>();
}
private class Unsubscriber : IDisposable
{
private List<IObserver<Temperature>> _observers;
private IObserver<Temperature> _observer;
public Unsubscriber(List<IObserver<Temperature>> observers, IObserver<Temperature> observer)
{
this._observers = observers;
this._observer = observer;
}
public void Dispose()
{
if (! (_observer == null)) _observers.Remove(_observer);
}
}
public IDisposable Subscribe(IObserver<Temperature> observer)
{
if (! observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
public void GetTemperature()
{
// Create an array of sample data to mimic a temperature device.
Nullable<Decimal>[] temps = {14.6m, 14.65m, 14.7m, 14.9m, 14.9m, 15.2m, 15.25m, 15.2m,
15.4m, 15.45m, null };
// Store the previous temperature, so notification is only sent after at least .1 change.
Nullable<Decimal> previous = null;
bool start = true;
foreach (var temp in temps) {
System.Threading.Thread.Sleep(2500);
if (temp.HasValue) {
if (start || (Math.Abs(temp.Value - previous.Value) >= 0.1m )) {
Temperature tempData = new Temperature(temp.Value, DateTime.Now);
foreach (var observer in observers)
observer.OnNext(tempData);
previous = temp;
if (start) start = false;
}
}
else {
foreach (var observer in observers.ToArray())
if (observer != null) observer.OnCompleted();
observers.Clear();
break;
}
}
}
}
Imports System.Threading
Imports System.Collections.Generic
Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
Dim observers As List(Of IObserver(Of Temperature))
Public Sub New()
observers = New List(Of IObserver(Of Temperature))
End Sub
Private Class Unsubscriber : Implements IDisposable
Private _observers As List(Of IObserver(Of Temperature))
Private _observer As IObserver(Of Temperature)
Public Sub New(ByVal observers As List(Of IObserver(Of Temperature)), ByVal observer As IObserver(Of Temperature))
Me._observers = observers
Me._observer = observer
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If _observer IsNot Nothing Then _observers.Remove(_observer)
End Sub
End Class
Public Function Subscribe(ByVal observer As System.IObserver(Of Temperature)) As System.IDisposable Implements System.IObservable(Of Temperature).Subscribe
If Not observers.Contains(observer) Then
observers.Add(observer)
End If
Return New Unsubscriber(observers, observer)
End Function
Public Sub GetTemperature()
' Create an array of sample data to mimic a temperature device.
Dim temps() As Nullable(Of Decimal) = {14.6D, 14.65D, 14.7D, 14.9D, 14.9D, 15.2D, 15.25D, 15.2D,
15.4D, 15.45D, Nothing}
' Store the previous temperature, so notification is only sent after at least .1 change.
Dim previous As Nullable(Of Decimal)
Dim start As Boolean = True
For Each temp In temps
System.Threading.Thread.Sleep(2500)
If temp.HasValue Then
If start OrElse Math.Abs(temp.Value - previous.Value) >= 0.1 Then
Dim tempData As New Temperature(temp.Value, Date.Now)
For Each observer In observers
observer.OnNext(tempData)
Next
previous = temp
If start Then start = False
End If
Else
For Each observer In observers.ToArray()
If observer IsNot Nothing Then observer.OnCompleted()
Next
observers.Clear()
Exit For
End If
Next
End Sub
End Class