Super einfaches Beispiel für C # Observer / Observable mit Delegierten

131

Ich habe kürzlich angefangen, mich mit C # zu beschäftigen, aber ich kann nicht anhand meines Lebens herausfinden, wie Delegierte arbeiten, wenn sie das Beobachter- / Beobachtungsmuster in der Sprache implementieren.

Könnte mir jemand ein supereinfaches Beispiel geben, wie es gemacht wird? Ich habe dies gegoogelt, aber alle Beispiele, die ich gefunden habe, waren entweder zu problemspezifisch oder zu "aufgebläht".

Deniz Dogan
quelle

Antworten:

218

Das Beobachtermuster wird normalerweise mit implementiert Ereignissen .

Hier ist ein Beispiel:

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();
    }
}

Weitere Informationen finden Sie im verlinkten Artikel.

Beachten Sie, dass im obigen Beispiel der nullbedingte C # 6 - Operator verwendet wird, DoSomethingum Fälle sicher zu implementieren , in denen er SomethingHappenednicht abonniert wurde und daher null ist. Wenn Sie eine ältere Version von C # verwenden, benötigen Sie folgenden Code:

public void DoSomething()
{
    var handler = SomethingHappened;
    if (handler != null)
    {
        handler(this, EventArgs.Empty);
    }
}
Jon Skeet
quelle
17
Um sich ein paar Zeilen zu sparen und die Nullprüfung zu
Dinah
1
@ Dinah: Das vermeidet nicht die Nullprüfung. Sie können SomethingHappened = nullspäter noch festlegen (eine praktische, wenn auch faule und nicht ideale Methode zum Abbestellen aller Handler), sodass die Nullprüfung immer erforderlich ist.
Dan Puzey
4
@DanPuzey: Sie können innerhalb der Klasse, aber Sie können auch sicherstellen, dass Sie das nicht tun - und anderer Code kann es nicht, da er nur abonnieren und abbestellen kann. Wenn Sie sicherstellen, dass Sie es in Ihrer Klasse niemals absichtlich auf null setzen, ist es in Ordnung, die Nullprüfung zu vermeiden.
Jon Skeet
2
@ JonSkeet: Natürlich habe ich vergessen, dass du das nicht außerhalb der Klasse machen kannst. Entschuldigung!
Dan Puzey
2
Ich denke, Sie können alle Sachen in DoSomething durchSomethingHappened?.Invoke(this, EventArgs.Empty);
Junior Mayhé
16

Hier ist ein einfaches Beispiel:

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;
    }
}

Hinweis:

  • Dies verstößt gegen eine Regel, da ich den Beobachter nicht vom Beobachtbaren aushake. Dies ist vielleicht gut genug für dieses einfache Beispiel, aber stellen Sie sicher, dass Sie die Beobachter nicht so von Ihren Ereignissen abhalten. Eine Möglichkeit, dies zu handhaben, besteht darin, ObserverClass IDisposable zu machen und die .Dispose-Methode das Gegenteil des Codes im Konstruktor tun zu lassen
  • Es wurde keine Fehlerprüfung durchgeführt, zumindest sollte im Konstruktor der ObserverClass eine Nullprüfung durchgeführt werden
Lasse V. Karlsen
quelle
15

In diesem Modell haben Sie Publisher, die Logik ausführen und ein "Ereignis" veröffentlichen.
Publisher senden ihre Veranstaltung dann nur an Abonnenten, die sich für den Empfang der jeweiligen Veranstaltung angemeldet haben.

In C # kann jedes Objekt eine Reihe von Ereignissen veröffentlichen, die andere Anwendungen abonnieren können.
Wenn die Veröffentlichungsklasse ein Ereignis auslöst, werden alle abonnierten Anwendungen benachrichtigt.
Die folgende Abbildung zeigt diesen Mechanismus.

Geben Sie hier die Bildbeschreibung ein

Einfachstes Beispiel für Ereignisse und Delegaten in C #:

Code ist selbsterklärend. Außerdem habe ich die Kommentare hinzugefügt, um den Code zu löschen.

  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
    }
}

Ausgabe:

Vom Zuhörer gehört

Habe es von Listener2 gehört

Vom Zuhörer gehört

Habe es von Listener2 gehört

Vom Zuhörer gehört. . . (unendlich oft)

JerryGoyal
quelle
6

Ich habe einige der oben genannten großartigen Beispiele (wie immer vielen Dank an Herrn Skeet und Herrn Karlsen ) zusammengefügt, um ein paar verschiedene Observables aufzunehmen, und eine Schnittstelle verwendet, um sie im Observer zu verfolgen, und dem Observer erlaubt, dies zu tun um eine beliebige Anzahl von Observablen über eine interne Liste zu "beobachten":

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);
        }
    }
}
Jesse C. Slicer
quelle
Ich weiß, das ist alt, aber ... Das sieht threadsicher aus, ist es aber nicht. Sowohl in Observer.Add als auch in Observer.Remove muss sich die Nullprüfung innerhalb der Sperre befinden. Dispose sollte auch die Sperre erwerben und ein isDispised-Flag setzen. Ansonsten ein gutes, vollständiges Beispiel.
user5151179
5

Das Anwenden des Beobachtermusters mit Delegaten und Ereignissen in c # wird benannt laut MSDN "Ereignismuster" bezeichnet was eine geringfügige Abweichung darstellt.

In diesem Artikel finden Sie gut strukturierte Beispiele für die Anwendung des Musters in c # sowohl auf klassische Weise als auch unter Verwendung von Delegaten und Ereignissen.

Erkunden des Observer-Entwurfsmusters

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
Anestis Kivranoglou
quelle
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);
            }
        }
Narottam Goyal
quelle
0

Ich wollte meinen Quellcode nicht ändern, um zusätzlichen Beobachter hinzuzufügen, daher habe ich folgendes einfaches Beispiel geschrieben:

//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 :)
    }
}
Imran Rizvi
quelle
0

Etwas wie das:

// 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)
    {
    }
}

. Beobachtermuster C # mit Ereignis . Link zum Repository

Elena K.
quelle