Linguagem
Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O padrão de desenho do observador requer uma divisão entre um fornecedor, que monitoriza os dados e envia notificações, e um ou mais observadores, que recebem notificações (callbacks) do fornecedor. Este artigo mostra como criar um fornecedor. Para informações sobre como criar um observador, veja Como implementar um observador.
Defina o tipo de dado
Defina os dados que o fornecedor envia aos observadores. Embora o fornecedor e os dados que envia aos observadores possam ser de um único tipo, um tipo diferente normalmente representa cada um. Por exemplo, numa aplicação de monitorização de temperatura, a Temperature estrutura define os dados que a TemperatureMonitor classe (definida na secção seguinte) monitoriza e aos quais subscrevem os observadores.
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
Criar um provedor
O fornecedor de dados é um tipo que implementa a System.IObservable<T> interface. O argumento de tipo genérico do provedor é o tipo que envia aos observadores.
Defina a classe do fornecedor. O exemplo seguinte define uma classe
TemperatureMonitor, que é uma implementação construída de System.IObservable<T> com um argumento de tipo genéricoTemperature.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)Adicione um campo para armazenar referências de observadores.
O fornecedor precisa de rastrear cada observador registado para poder enviar notificações mais tarde. Normalmente, utiliza-se um objeto de coleção, como um objeto genérico List<T> . O exemplo seguinte define um objeto privado List<T> instanciado no
TemperatureMonitorconstrutor de classes.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()Defina uma IDisposable implementação para cancelar a subscrição.
O fornecedor devolve esta implementação aos assinantes para que estes possam deixar de receber notificações a qualquer momento. O exemplo seguinte define uma classe aninhada
Unsubscriberque recebe uma referência para a coleção de subscritores e para o subscritor quando é instanciada. AUnsubscriberclasse permite ao assinante chamar a implementação do IDisposable.Dispose objeto para se remover da coleção dos subscritores.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 ClassImplemente o método IObservable<T>.Subscribe.
O método recebe uma referência à System.IObserver<T> interface. Armazene essa referência na coleção de observadores do passo anterior e, em seguida, devolva a implementação de cancelamento da subscrição IDisposable. O exemplo seguinte mostra a
Subscribeimplementação naTemperatureMonitorclasse.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 FunctionImplemente a lógica de notificação ao chamar os métodos IObserver<T>.OnNext, IObserver<T>.OnError e IObserver<T>.OnCompleted dos observadores.
Em alguns casos, um fornecedor pode não invocar OnError quando ocorre um erro. O método seguinte
GetTemperaturesimula um monitor que lê dados de temperatura a cada cinco segundos e notifica os observadores se a temperatura mudou pelo menos 0,1 grau desde a leitura anterior. Se o dispositivo não indicar uma temperatura (ou seja, se o respetivo valor for nulo), o fornecedor notifica os observadores de que a transmissão está concluída, chamando o método OnCompleted de cada observador, e limpa a coleção List<T>. Neste exemplo, o fornecedor nunca chama OnError.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
Example
O exemplo seguinte contém o código-fonte completo de uma IObservable<T> implementação para uma aplicação de monitorização de temperatura. Inclui a Temperature estrutura, que são os dados que o fornecedor envia aos observadores, e a TemperatureMonitor classe, que é a IObservable<T> implementação.
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