Langage
Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Le modèle de conception d’observateur nécessite une division entre un fournisseur, qui surveille les données et envoie des notifications, et un ou plusieurs observateurs, qui reçoivent des notifications (rappels) du fournisseur. Cet article explique comment créer un fournisseur. Pour plus d’informations sur la création d’un observateur, consultez Comment implémenter un observateur.
Définir le type de données
Définissez les données envoyées par le fournisseur aux observateurs. Bien que le fournisseur et les données qu’il envoie aux observateurs peuvent être un type unique, un type différent représente généralement chacun d’eux. Par exemple, dans une application de surveillance de température, la Temperature structure définit les données que la TemperatureMonitor classe (définie dans la section suivante) surveille et à quels observateurs s’abonnent.
namespace TemperatureSample;
public readonly record struct Temperature(decimal Degrees, DateTime Date);
Namespace Global.TemperatureSample
Public Structure Temperature
Public ReadOnly Property Degrees As Decimal
Public ReadOnly Property [Date] As Date
Public Sub New(degrees As Decimal, [date] As Date)
Me.Degrees = degrees
Me.Date = [date]
End Sub
End Structure
End Namespace
Créer un fournisseur
Le fournisseur de données est un type qui implémente l’interface System.IObservable<T> . L’argument de type générique du fournisseur est le type qu’il envoie aux observateurs.
Définissez la classe de fournisseur. L’exemple suivant définit une
TemperatureMonitorclasse, qui est une implémentation construite System.IObservable<T> avec un argument de type générique deTemperature.namespace TemperatureSample; public sealed class TemperatureMonitor : IObservable<Temperature> {Imports System.Threading Imports System.Threading.Tasks Namespace Global.TemperatureSample Public NotInheritable Class TemperatureMonitor Implements IObservable(Of Temperature)Ajoutez un champ pour stocker les références d’observateur.
Le fournisseur doit suivre chaque observateur inscrit afin qu’il puisse envoyer des notifications ultérieurement. En règle générale, utilisez un objet de collection tel qu’un objet générique List<T> . L’exemple suivant définit un objet privé List<T> instancié dans le constructeur de
TemperatureMonitorclasse.namespace TemperatureSample; public sealed class TemperatureMonitor : IObservable<Temperature> { private readonly List<IObserver<Temperature>> _observers = []; private readonly Lock _sync = new();Imports System.Threading Imports System.Threading.Tasks Namespace Global.TemperatureSample Public NotInheritable Class TemperatureMonitor Implements IObservable(Of Temperature) Private ReadOnly _observers As New List(Of IObserver(Of Temperature))() Private ReadOnly _sync As New Object()Définissez une implémentation IDisposable pour le désabonnement.
Le fournisseur retourne cette implémentation aux abonnés afin qu’ils puissent cesser de recevoir des notifications à tout moment. L’exemple suivant définit une classe imbriquée
Unsubscriberqui reçoit une référence à la collection d’abonnés et à l’abonné lorsqu’elle est instanciée. La classeUnsubscriberpermet à l’abonné d’appeler l’implémentation de IDisposable.Dispose de l’objet pour se retirer de la collection des abonnés.private sealed class Unsubscriber( List<IObserver<Temperature>> observers, IObserver<Temperature> observer, Lock sync) : IDisposable { public void Dispose() { lock (sync) { observers.Remove(observer); } } }Private NotInheritable Class Unsubscriber Implements IDisposable Private ReadOnly _observers As List(Of IObserver(Of Temperature)) Private ReadOnly _observer As IObserver(Of Temperature) Private ReadOnly _sync As Object Public Sub New(observers As List(Of IObserver(Of Temperature)), observer As IObserver(Of Temperature), sync As Object) _observers = observers _observer = observer _sync = sync End Sub Public Sub Dispose() Implements IDisposable.Dispose SyncLock _sync _observers.Remove(_observer) End SyncLock End Sub End ClassImplémentez la méthode IObservable<T>.Subscribe.
La méthode reçoit une référence à l’interface System.IObserver<T> . Stockez cette référence dans la collection d’observateurs de l’étape précédente, puis retournez l’implémentation de désabonnement IDisposable. L’exemple suivant montre l’implémentation
Subscribedans laTemperatureMonitorclasse.public IDisposable Subscribe(IObserver<Temperature> observer) { ArgumentNullException.ThrowIfNull(observer); lock (_sync) { if (!_observers.Contains(observer)) _observers.Add(observer); } return new Unsubscriber(_observers, observer, _sync); }Public Function Subscribe(observer As IObserver(Of Temperature)) As IDisposable _ Implements IObservable(Of Temperature).Subscribe ArgumentNullException.ThrowIfNull(observer) SyncLock _sync If Not _observers.Contains(observer) Then _observers.Add(observer) End If End SyncLock Return New Unsubscriber(_observers, observer, _sync) End FunctionImplémentez la logique de notification en appelant les méthodes IObserver<T>.OnNext, IObserver<T>.OnError et IObserver<T>.OnCompleted des observateurs.
Dans certains cas, un fournisseur peut ne pas appeler OnError lorsqu’une erreur se produit. La méthode suivante
GetTemperaturesimule un moniteur qui lit les données de température toutes les cinq secondes et avertit les observateurs si la température a changé d’au moins .1 degré depuis la lecture précédente. Si l’appareil ne signale pas de température (autrement dit, si sa valeur est null), le fournisseur avertit les observateurs que la transmission est terminée en appelant la méthode de OnCompleted chaque observateur et efface la List<T> collection. Dans cet exemple, le fournisseur n’appelle OnErrorjamais .public async Task GetTemperatureAsync(CancellationToken cancellationToken = default) { // Sample data that mimics a temperature device. A null value signals the end of transmission. decimal?[] temps = [ 14.6m, 14.65m, 14.7m, 14.9m, 14.9m, 15.2m, 15.25m, 15.2m, 15.4m, 15.45m, null ]; decimal? previous = null; foreach (decimal? temp in temps) { await Task.Delay(TimeSpan.FromSeconds(2.5), cancellationToken); if (temp is decimal value) { // Notify only after at least a 0.1° change. if (previous is null || Math.Abs(value - previous.Value) >= 0.1m) { NotifyAll(new Temperature(value, DateTime.Now)); previous = value; } } else { CompleteAll(); break; } } } private void NotifyAll(Temperature data) { IObserver<Temperature>[] snapshot; lock (_sync) { snapshot = [.. _observers]; } foreach (IObserver<Temperature> observer in snapshot) observer.OnNext(data); } private void CompleteAll() { IObserver<Temperature>[] snapshot; lock (_sync) { snapshot = [.. _observers]; _observers.Clear(); } foreach (IObserver<Temperature> observer in snapshot) observer.OnCompleted(); }Public Async Function GetTemperatureAsync(Optional cancellationToken As CancellationToken = Nothing) As Task ' Sample data that mimics a temperature device. A Nothing value signals the end of transmission. Dim temps As Decimal?() = { 14.6D, 14.65D, 14.7D, 14.9D, 14.9D, 15.2D, 15.25D, 15.2D, 15.4D, 15.45D, Nothing } Dim previous As Decimal? = Nothing For Each temp As Decimal? In temps Await Task.Delay(TimeSpan.FromSeconds(2.5), cancellationToken) If temp.HasValue Then ' Notify only after at least a 0.1° change. If Not previous.HasValue OrElse Math.Abs(temp.Value - previous.Value) >= 0.1D Then NotifyAll(New Temperature(temp.Value, Date.Now)) previous = temp End If Else CompleteAll() Exit For End If Next End Function Private Sub NotifyAll(data As Temperature) Dim snapshot As IObserver(Of Temperature)() SyncLock _sync snapshot = _observers.ToArray() End SyncLock For Each observer In snapshot observer.OnNext(data) Next End Sub Private Sub CompleteAll() Dim snapshot As IObserver(Of Temperature)() SyncLock _sync snapshot = _observers.ToArray() _observers.Clear() End SyncLock For Each observer In snapshot observer.OnCompleted() Next End Sub
Exemple
L’exemple suivant contient le code source complet d’une IObservable<T> implémentation pour une application de surveillance de la température. Il inclut la Temperature structure, qui est les données envoyées par le fournisseur aux observateurs, et la TemperatureMonitor classe, qui est l’implémentation IObservable<T> .
namespace TemperatureSample;
public sealed class TemperatureMonitor : IObservable<Temperature>
{
private readonly List<IObserver<Temperature>> _observers = [];
private readonly Lock _sync = new();
private sealed class Unsubscriber(
List<IObserver<Temperature>> observers,
IObserver<Temperature> observer,
Lock sync) : IDisposable
{
public void Dispose()
{
lock (sync)
{
observers.Remove(observer);
}
}
}
public IDisposable Subscribe(IObserver<Temperature> observer)
{
ArgumentNullException.ThrowIfNull(observer);
lock (_sync)
{
if (!_observers.Contains(observer))
_observers.Add(observer);
}
return new Unsubscriber(_observers, observer, _sync);
}
public async Task GetTemperatureAsync(CancellationToken cancellationToken = default)
{
// Sample data that mimics a temperature device. A null value signals the end of transmission.
decimal?[] temps =
[
14.6m, 14.65m, 14.7m, 14.9m, 14.9m, 15.2m,
15.25m, 15.2m, 15.4m, 15.45m, null
];
decimal? previous = null;
foreach (decimal? temp in temps)
{
await Task.Delay(TimeSpan.FromSeconds(2.5), cancellationToken);
if (temp is decimal value)
{
// Notify only after at least a 0.1° change.
if (previous is null || Math.Abs(value - previous.Value) >= 0.1m)
{
NotifyAll(new Temperature(value, DateTime.Now));
previous = value;
}
}
else
{
CompleteAll();
break;
}
}
}
private void NotifyAll(Temperature data)
{
IObserver<Temperature>[] snapshot;
lock (_sync)
{
snapshot = [.. _observers];
}
foreach (IObserver<Temperature> observer in snapshot)
observer.OnNext(data);
}
private void CompleteAll()
{
IObserver<Temperature>[] snapshot;
lock (_sync)
{
snapshot = [.. _observers];
_observers.Clear();
}
foreach (IObserver<Temperature> observer in snapshot)
observer.OnCompleted();
}
}
Imports System.Threading
Imports System.Threading.Tasks
Namespace Global.TemperatureSample
Public NotInheritable Class TemperatureMonitor
Implements IObservable(Of Temperature)
Private ReadOnly _observers As New List(Of IObserver(Of Temperature))()
Private ReadOnly _sync As New Object()
Private NotInheritable Class Unsubscriber
Implements IDisposable
Private ReadOnly _observers As List(Of IObserver(Of Temperature))
Private ReadOnly _observer As IObserver(Of Temperature)
Private ReadOnly _sync As Object
Public Sub New(observers As List(Of IObserver(Of Temperature)),
observer As IObserver(Of Temperature),
sync As Object)
_observers = observers
_observer = observer
_sync = sync
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
SyncLock _sync
_observers.Remove(_observer)
End SyncLock
End Sub
End Class
Public Function Subscribe(observer As IObserver(Of Temperature)) As IDisposable _
Implements IObservable(Of Temperature).Subscribe
ArgumentNullException.ThrowIfNull(observer)
SyncLock _sync
If Not _observers.Contains(observer) Then
_observers.Add(observer)
End If
End SyncLock
Return New Unsubscriber(_observers, observer, _sync)
End Function
Public Async Function GetTemperatureAsync(Optional cancellationToken As CancellationToken = Nothing) As Task
' Sample data that mimics a temperature device. A Nothing value signals the end of transmission.
Dim temps As Decimal?() = {
14.6D, 14.65D, 14.7D, 14.9D, 14.9D, 15.2D,
15.25D, 15.2D, 15.4D, 15.45D, Nothing
}
Dim previous As Decimal? = Nothing
For Each temp As Decimal? In temps
Await Task.Delay(TimeSpan.FromSeconds(2.5), cancellationToken)
If temp.HasValue Then
' Notify only after at least a 0.1° change.
If Not previous.HasValue OrElse Math.Abs(temp.Value - previous.Value) >= 0.1D Then
NotifyAll(New Temperature(temp.Value, Date.Now))
previous = temp
End If
Else
CompleteAll()
Exit For
End If
Next
End Function
Private Sub NotifyAll(data As Temperature)
Dim snapshot As IObserver(Of Temperature)()
SyncLock _sync
snapshot = _observers.ToArray()
End SyncLock
For Each observer In snapshot
observer.OnNext(data)
Next
End Sub
Private Sub CompleteAll()
Dim snapshot As IObserver(Of Temperature)()
SyncLock _sync
snapshot = _observers.ToArray()
_observers.Clear()
End SyncLock
For Each observer In snapshot
observer.OnCompleted()
Next
End Sub
End Class
End Namespace