Gör så här: Implementera en provider
Mönstret för observatörsdesign kräver en uppdelning mellan en leverantör, som övervakar data och skickar meddelanden, och en eller flera observatörer som tar emot meddelanden (återanrop) från leverantören. I det här avsnittet beskrivs hur du skapar en provider. Ett relaterat ämne, How to: Implement an Observer, diskuterar hur du skapar en observatör.
Så här skapar du en leverantör
Definiera de data som providern ansvarar för att skicka till observatörer. Även om providern och de data som den skickar till observatörer kan vara en enda typ, representeras de i allmänhet av olika typer. I ett temperaturövervakningsprogram
Temperature
definierar strukturen till exempel de data som providern (som representeras av klassenTemperatureMonitor
som definieras i nästa steg) övervakar och som observatörer prenumererar på.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
Definiera dataprovidern, som är en typ som implementerar System.IObservable<T> gränssnittet. Providerns allmänna typargument är den typ som providern skickar till observatörer. I följande exempel definieras en
TemperatureMonitor
klass, som är en konstruerad System.IObservable<T> implementering med ett allmänt typargument avTemperature
.using System; using System.Collections.Generic; public class TemperatureMonitor : IObservable<Temperature> {
Imports System.Collections.Generic Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
Bestäm hur leverantören ska lagra referenser till observatörer så att varje observatör kan meddelas när det är lämpligt. Oftast används ett samlingsobjekt, till exempel ett allmänt List<T> objekt, för detta ändamål. I följande exempel definieras ett privat List<T> objekt som instansieras i
TemperatureMonitor
klasskonstruktorn.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
Definiera en IDisposable implementering som leverantören kan återgå till prenumeranter så att de kan sluta ta emot meddelanden när som helst. I följande exempel definieras en kapslad
Unsubscriber
klass som skickas en referens till prenumerantsamlingen och till prenumeranten när klassen instansieras. Med den här koden kan prenumeranten anropa objektets IDisposable.Dispose implementering för att ta bort sig själv från prenumerantsamlingen.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
IObservable<T>.Subscribe Implementera metoden. Metoden skickas en referens till System.IObserver<T> gränssnittet och ska lagras i objektet som utformats för det ändamålet i steg 3. Metoden bör sedan returnera implementeringen IDisposable som utvecklades i steg 4. I följande exempel visas implementeringen av Subscribe metoden i
TemperatureMonitor
klassen.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
Meddela observatörer efter behov genom att anropa deras IObserver<T>.OnNext, IObserver<T>.OnErroroch IObserver<T>.OnCompleted implementeringar. I vissa fall kanske en provider inte anropar OnError metoden när ett fel inträffar. Följande
GetTemperature
metod simulerar till exempel en övervakare som läser temperaturdata var femte sekund och meddelar observatörer om temperaturen har ändrats med minst 1 grad sedan föregående läsning. Om enheten inte rapporterar någon temperatur (dvs. om dess värde är null) meddelar leverantören observatörerna att överföringen är klar. Observera att förutom att anropa varje observatörs OnCompleted metodGetTemperature
rensar List<T> metoden samlingen. I det här fallet gör leverantören inga anrop till OnError sina observatörers metod.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
Exempel
Följande exempel innehåller den fullständiga källkoden för att definiera en IObservable<T> implementering för ett temperaturövervakningsprogram. Den innehåller Temperature
strukturen, som är de data som skickas till observatörer, och TemperatureMonitor
klassen, vilket är implementeringen IObservable<T> .
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