Esempio semplicissimo di osservatore C # / osservabile con i delegati


131

Di recente ho iniziato a scavare in C # ma per la mia vita non riesco a capire come funzionano i delegati quando implementano il modello osservatore / osservabile nella lingua.

Qualcuno potrebbe darmi un esempio semplicissimo di come viene fatto? Io ho Googled questo, ma tutti gli esempi che ho trovato erano o troppo problema specifico o troppo "gonfio".

Risposte:


218

Il modello di osservatore viene solitamente implementato con eventi .

Ecco un esempio:

using System;

class Observable
{
    public event EventHandler SomethingHappened;

    public void DoSomething() =>
        SomethingHappened?.Invoke(this, EventArgs.Empty);
}

class Observer
{
    public void HandleEvent(object sender, EventArgs args)
    {
        Console.WriteLine("Something happened to " + sender);
    }
}

class Test
{
    static void Main()
    {
        Observable observable = new Observable();
        Observer observer = new Observer();
        observable.SomethingHappened += observer.HandleEvent;

        observable.DoSomething();
    }
}

Vedi l'articolo collegato per ulteriori dettagli.

Si noti che l'esempio precedente utilizza l' operatore null-condizionale C # 6 per implementare in DoSomethingmodo sicuro per gestire i casi in cui SomethingHappenednon è stato sottoscritto ed è quindi nullo. Se stai usando una versione precedente di C #, avresti bisogno di un codice come questo:

public void DoSomething()
{
    var handler = SomethingHappened;
    if (handler != null)
    {
        handler(this, EventArgs.Empty);
    }
}

17
Per salvarti qualche riga ed evitare il controllo null, inizializza il tuo evento in questo modo: stackoverflow.com/questions/340610/…
Dinah

1
@Dinah: questo non evita il controllo null. Puoi comunque impostare in SomethingHappened = nullseguito (un modo utile se pigro e non ideale di annullare l'iscrizione a tutti i gestori), quindi il controllo null è sempre necessario.
Dan Puzey,

4
@DanPuzey: puoi all'interno della classe, ma allo stesso modo puoi assicurarti di non farlo - e l' altro codice non può farlo, poiché può solo iscriversi e annullare l'iscrizione. Se ti assicuri di non impostarlo mai su null deliberatamente all'interno della tua classe, è bene evitare il controllo null.
Jon Skeet,

2
@JonSkeet: ovviamente mi stavo dimenticando che non puoi farlo al di fuori della lezione. Scuse!
Dan Puzey,

2
Penso che puoi sostituire tutte le cose in DoSomething conSomethingHappened?.Invoke(this, EventArgs.Empty);
Junior Mayhé il

16

Ecco un semplice esempio:

public class ObservableClass
{
    private Int32 _Value;

    public Int32 Value
    {
        get { return _Value; }
        set
        {
            if (_Value != value)
            {
                _Value = value;
                OnValueChanged();
            }
        }
    }

    public event EventHandler ValueChanged;

    protected void OnValueChanged()
    {
        if (ValueChanged != null)
            ValueChanged(this, EventArgs.Empty);
    }
}

public class ObserverClass
{
    public ObserverClass(ObservableClass observable)
    {
        observable.ValueChanged += TheValueChanged;
    }

    private void TheValueChanged(Object sender, EventArgs e)
    {
        Console.Out.WriteLine("Value changed to " +
            ((ObservableClass)sender).Value);
    }
}

public class Program
{
    public static void Main()
    {
        ObservableClass observable = new ObservableClass();
        ObserverClass observer = new ObserverClass(observable);
        observable.Value = 10;
    }
}

Nota:

  • Ciò viola una regola in quanto non sgancio l'osservatore dall'osservabile, questo è forse abbastanza buono per questo semplice esempio, ma assicurati di non tenere gli osservatori sospesi fuori dai tuoi eventi in quel modo. Un modo per gestirlo sarebbe rendere ObserverClass IDisposable e lasciare che il metodo .Dispose faccia l'opposto del codice nel costruttore
  • Nessun controllo degli errori eseguito, almeno un controllo null dovrebbe essere eseguito nel costruttore di ObserverClass

15

In questo modello, hai editori che faranno un po 'di logica e pubblicheranno un "evento".
Gli editori invieranno quindi il loro evento solo agli abbonati che si sono abbonati per ricevere l'evento specifico.

In C #, qualsiasi oggetto può pubblicare una serie di eventi a cui possono iscriversi altre applicazioni.
Quando la classe editoriale pubblica un evento, tutte le applicazioni sottoscritte vengono avvisate.
La figura seguente mostra questo meccanismo.

inserisci qui la descrizione dell'immagine

Il più semplice esempio possibile su eventi e delegati in C #:

il codice è autoesplicativo, inoltre ho aggiunto i commenti per cancellare il codice.

  using System;

public class Publisher //main publisher class which will invoke methods of all subscriber classes
{
    public delegate void TickHandler(Publisher m, EventArgs e); //declaring a delegate
    public TickHandler Tick;     //creating an object of delegate
    public EventArgs e = null;   //set 2nd paramter empty
    public void Start()     //starting point of thread
    {
        while (true)
        {
            System.Threading.Thread.Sleep(300);
            if (Tick != null)   //check if delegate object points to any listener classes method
            {
                Tick(this, e);  //if it points i.e. not null then invoke that method!
            }
        }
    }
}

public class Subscriber1                //1st subscriber class
{
    public void Subscribe(Publisher m)  //get the object of pubisher class
    {
        m.Tick += HeardIt;              //attach listener class method to publisher class delegate object
    }
    private void HeardIt(Publisher m, EventArgs e)   //subscriber class method
    {
        System.Console.WriteLine("Heard It by Listener");
    }

}
public class Subscriber2                   //2nd subscriber class
{
    public void Subscribe2(Publisher m)    //get the object of pubisher class
    {
        m.Tick += HeardIt;               //attach listener class method to publisher class delegate object
    }
    private void HeardIt(Publisher m, EventArgs e)   //subscriber class method
    {
        System.Console.WriteLine("Heard It by Listener2");
    }

}

class Test
{
    static void Main()
    {
        Publisher m = new Publisher();      //create an object of publisher class which will later be passed on subscriber classes
        Subscriber1 l = new Subscriber1();  //create object of 1st subscriber class
        Subscriber2 l2 = new Subscriber2(); //create object of 2nd subscriber class
        l.Subscribe(m);     //we pass object of publisher class to access delegate of publisher class
        l2.Subscribe2(m);   //we pass object of publisher class to access delegate of publisher class

        m.Start();          //starting point of publisher class
    }
}

Produzione:

Sentito dall'ascoltatore

Sentito da Listener2

Sentito dall'ascoltatore

Sentito da Listener2

Sentito dall'ascoltatore. . . (tempi infiniti)


6

Ho messo insieme un paio dei grandi esempi di cui sopra (grazie come sempre al Sig. Skeet e al Sig. Karlsen ) per includere un paio di Osservabili diversi e ho utilizzato un'interfaccia per tenerne traccia nell'Osservatore e ho permesso all'Osservatore di "osservare" un numero qualsiasi di osservabili tramite un elenco interno:

namespace ObservablePattern
{
    using System;
    using System.Collections.Generic;

    internal static class Program
    {
        private static void Main()
        {
            var observable = new Observable();
            var anotherObservable = new AnotherObservable();

            using (IObserver observer = new Observer(observable))
            {
                observable.DoSomething();
                observer.Add(anotherObservable);
                anotherObservable.DoSomething();
            }

            Console.ReadLine();
        }
    }

    internal interface IObservable
    {
        event EventHandler SomethingHappened;
    }

    internal sealed class Observable : IObservable
    {
        public event EventHandler SomethingHappened;

        public void DoSomething()
        {
            var handler = this.SomethingHappened;

            Console.WriteLine("About to do something.");
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }

    internal sealed class AnotherObservable : IObservable
    {
        public event EventHandler SomethingHappened;

        public void DoSomething()
        {
            var handler = this.SomethingHappened;

            Console.WriteLine("About to do something different.");
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }

    internal interface IObserver : IDisposable
    {
        void Add(IObservable observable);

        void Remove(IObservable observable);
    }

    internal sealed class Observer : IObserver
    {
        private readonly Lazy<IList<IObservable>> observables =
            new Lazy<IList<IObservable>>(() => new List<IObservable>());

        public Observer()
        {
        }

        public Observer(IObservable observable) : this()
        {
            this.Add(observable);
        }

        public void Add(IObservable observable)
        {
            if (observable == null)
            {
                return;
            }

            lock (this.observables)
            {
                this.observables.Value.Add(observable);
                observable.SomethingHappened += HandleEvent;
            }
        }

        public void Remove(IObservable observable)
        {
            if (observable == null)
            {
                return;
            }

            lock (this.observables)
            {
                observable.SomethingHappened -= HandleEvent;
                this.observables.Value.Remove(observable);
            }
        }

        public void Dispose()
        {
            for (var i = this.observables.Value.Count - 1; i >= 0; i--)
            {
                this.Remove(this.observables.Value[i]);
            }
        }

        private static void HandleEvent(object sender, EventArgs args)
        {
            Console.WriteLine("Something happened to " + sender);
        }
    }
}

So che questo è vecchio, ma ... Sembra sicuro, ma non lo è. In Observer.Add e Observer.Remove il controllo null deve essere all'interno del blocco. Dispose dovrebbe anche acquisire il lucchetto e impostare un flag isDispised. Altrimenti un buon esempio completo.
user5151179

5

L'applicazione del modello di osservatore con delegati ed eventi in c # è denominata "Modello di evento" in base a MSDN, il che rappresenta una leggera variazione.

In questo articolo troverai esempi ben strutturati di come applicare lo schema in c # sia nel modo classico sia usando delegati ed eventi.

Esplorazione del modello di progettazione dell'osservatore

public class Stock
{

    //declare a delegate for the event
    public delegate void AskPriceChangedHandler(object sender,
          AskPriceChangedEventArgs e);
    //declare the event using the delegate
    public event AskPriceChangedHandler AskPriceChanged;

    //instance variable for ask price
    object _askPrice;

    //property for ask price
    public object AskPrice
    {

        set
        {
            //set the instance variable
            _askPrice = value;

            //fire the event
            OnAskPriceChanged();
        }

    }//AskPrice property

    //method to fire event delegate with proper name
    protected void OnAskPriceChanged()
    {

        AskPriceChanged(this, new AskPriceChangedEventArgs(_askPrice));

    }//AskPriceChanged

}//Stock class

//specialized event class for the askpricechanged event
public class AskPriceChangedEventArgs : EventArgs
{

    //instance variable to store the ask price
    private object _askPrice;

    //constructor that sets askprice
    public AskPriceChangedEventArgs(object askPrice) { _askPrice = askPrice; }

    //public property for the ask price
    public object AskPrice { get { return _askPrice; } }

}//AskPriceChangedEventArgs

1
    /**********************Simple Example ***********************/    

class Program
        {
            static void Main(string[] args)
            {
                Parent p = new Parent();
            }
        }

        ////////////////////////////////////////////

        public delegate void DelegateName(string data);

        class Child
        {
            public event DelegateName delegateName;

            public void call()
            {
                delegateName("Narottam");
            }
        }

        ///////////////////////////////////////////

        class Parent
        {
            public Parent()
            {
                Child c = new Child();
                c.delegateName += new DelegateName(print);
                //or like this
                //c.delegateName += print;
                c.call();
            }

            public void print(string name)
            {
                Console.WriteLine("yes we got the name : " + name);
            }
        }

0

Non volevo cambiare il mio codice sorgente per aggiungere osservatore aggiuntivo, quindi ho scritto il seguente esempio semplice:

//EVENT DRIVEN OBSERVER PATTERN
public class Publisher
{
    public Publisher()
    {
        var observable = new Observable();
        observable.PublishData("Hello World!");
    }
}

//Server will send data to this class's PublishData method
public class Observable
{
    public event Receive OnReceive;

    public void PublishData(string data)
    {
        //Add all the observer below
        //1st observer
        IObserver iObserver = new Observer1();
        this.OnReceive += iObserver.ReceiveData;
        //2nd observer
        IObserver iObserver2 = new Observer2();
        this.OnReceive += iObserver2.ReceiveData;

        //publish data 
        var handler = OnReceive;
        if (handler != null)
        {
            handler(data);
        }
    }
}

public interface IObserver
{
    void ReceiveData(string data);
}

//Observer example
public class Observer1 : IObserver
{
    public void ReceiveData(string data)
    {
        //sample observers does nothing with data :)
    }
}

public class Observer2 : IObserver
{
    public void ReceiveData(string data)
    {
        //sample observers does nothing with data :)
    }
}

0

Qualcosa come questo:

// interface implementation publisher
public delegate void eiSubjectEventHandler(eiSubject subject);

public interface eiSubject
{
    event eiSubjectEventHandler OnUpdate;

    void GenereteEventUpdate();

}

// class implementation publisher
class ecSubject : eiSubject
{
    private event eiSubjectEventHandler _OnUpdate = null;
    public event eiSubjectEventHandler OnUpdate
    {
        add
        {
            lock (this)
            {
                _OnUpdate -= value;
                _OnUpdate += value;
            }
        }
        remove { lock (this) { _OnUpdate -= value; } }
    }

    public void GenereteEventUpdate()
    {
        eiSubjectEventHandler handler = _OnUpdate;

        if (handler != null)
        {
            handler(this);
        }
    }

}

// interface implementation subscriber
public interface eiObserver
{
    void DoOnUpdate(eiSubject subject);

}

// class implementation subscriber
class ecObserver : eiObserver
{
    public virtual void DoOnUpdate(eiSubject subject)
    {
    }
}

. modello osservatore C # con evento . collegamento al repository

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.