Megosztás a következőn keresztül:


Útmutató: Szolgáltató implementálása

A megfigyelő kialakítási mintája megköveteli egy olyan szolgáltató megosztását, amely figyeli az adatokat, és értesítéseket küld, valamint egy vagy több megfigyelőt, amelyek értesítéseket (visszahívásokat) kapnak a szolgáltatótól. Ez a témakör bemutatja, hogyan hozhat létre szolgáltatót. Egy kapcsolódó témakör, a How to: Implement an Observer (Megfigyelő implementálása) című témakör azt ismerteti, hogyan hozhat létre megfigyelőt.

Szolgáltató létrehozása

  1. Határozza meg a szolgáltató által a megfigyelőknek küldött adatokat. Bár a szolgáltató és a megfigyelőknek küldött adatok lehetnek egyetlen típusok, általában különböző típusok képviselik őket. Egy hőmérséklet-monitorozási alkalmazásban például a Temperature struktúra határozza meg azokat az adatokat, amelyeket a szolgáltató (amelyet a TemperatureMonitor következő lépésben meghatározott osztály jelöl) figyel, és amelyekre a megfigyelők előfizetnek.

    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
    
  2. Adja meg az adatszolgáltatót, amely az interfészt megvalósító System.IObservable<T> típus. A szolgáltató általános típusargumentuma az a típus, amelyet a szolgáltató küld a megfigyelőknek. Az alábbi példa egy osztályt TemperatureMonitor határoz meg, amely egy konstruktált System.IObservable<T> implementáció, amely a következő általános típusú argumentummal rendelkezik Temperature: .

    using System;
    using System.Collections.Generic;
    
    public class TemperatureMonitor : IObservable<Temperature>
    {
    
    Imports System.Collections.Generic
    
    
    Public Class TemperatureMonitor : Implements IObservable(Of Temperature)
    
  3. Annak meghatározása, hogy a szolgáltató hogyan tárolja a megfigyelőkre mutató hivatkozásokat, hogy az egyes megfigyelők szükség esetén értesülhessenek. Erre a célra leggyakrabban gyűjteményobjektumot, például általános List<T> objektumot használnak. Az alábbi példa az osztálykonstruktorban TemperatureMonitor példányosított privát List<T> objektumot definiál.

    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
    
  4. Definiáljon egy implementációt IDisposable , amelyet a szolgáltató visszatérhet az előfizetőkhöz, hogy bármikor leállíthassák az értesítések fogadását. Az alábbi példa egy beágyazott Unsubscriber osztályt határoz meg, amely az előfizetői gyűjteményre és az előfizetőre mutató hivatkozást adja át az osztály példányosításakor. Ez a kód lehetővé teszi, hogy az előfizető meghívja az objektum implementációját IDisposable.Dispose , hogy eltávolítsa magát az előfizetői gyűjteményből.

    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
    
  5. Implementálja a metódust IObservable<T>.Subscribe . A metódus az interfészre System.IObserver<T> mutató hivatkozást ad át, és a 3. lépésben az erre a célra tervezett objektumban kell tárolni. A metódusnak ezután vissza kell adnia a IDisposable 4. lépésben kifejlesztett implementációt. Az alábbi példa a metódus alkalmazását Subscribe mutatja be az TemperatureMonitor osztályban.

    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
    
  6. A megfigyelőket szükség szerint értesítheti a saját , IObserver<T>.OnErrorés IObserver<T>.OnCompleted implementációik IObserver<T>.OnNextmeghívásával. Bizonyos esetekben előfordulhat, hogy a szolgáltató nem hívja meg a OnError metódust hiba esetén. Az alábbi GetTemperature módszer például egy monitort szimulál, amely öt másodpercenként olvassa be a hőmérsékleti adatokat, és értesíti a megfigyelőket, ha a hőmérséklet legalább 0,1 fokkal változott az előző olvasás óta. Ha az eszköz nem jelent hőmérsékletet (vagyis ha értéke null), a szolgáltató értesíti a megfigyelőket, hogy az átvitel befejeződött. Vegye figyelembe, hogy az egyes megfigyelők metódusának meghívása OnCompleted mellett a GetTemperature módszer törli a gyűjteményt List<T> . Ebben az esetben a szolgáltató nem hívja meg megfigyelői OnError módszerét.

    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
    

Példa

Az alábbi példa a hőmérséklet-monitorozási alkalmazás implementációjának meghatározásához IObservable<T> szükséges teljes forráskódot tartalmazza. Ez magában foglalja a struktúrát Temperature , amely a megfigyelőknek küldött adatok, és az TemperatureMonitor osztály, amely a IObservable<T> megvalósítás.

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

Lásd még