Contoh super-sederhana dari pengamat C # / diamati dengan delegasi


131

Saya baru-baru ini mulai menggali ke dalam C # tetapi saya tidak bisa dengan hidup saya mencari tahu bagaimana delegasi bekerja ketika menerapkan pengamat / pola yang dapat diamati dalam bahasa.

Bisakah seseorang memberi saya contoh super sederhana tentang bagaimana hal itu dilakukan? Saya telah mencarinya di Google, tetapi semua contoh yang saya temukan terlalu spesifik masalah atau terlalu "kembung".

Jawaban:


218

Pola pengamat biasanya diterapkan dengan acara .

Ini sebuah contoh:

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

Lihat artikel tertaut untuk detail lebih lanjut.

Perhatikan bahwa contoh di atas menggunakan operator bersyarat nol C # 6 untuk menerapkan DoSomethingdengan aman untuk menangani kasus-kasus di mana SomethingHappenedbelum berlangganan, dan karena itu nol. Jika Anda menggunakan versi C # yang lebih lama, Anda perlu kode seperti ini:

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

17
Untuk menyelamatkan diri Anda dari beberapa baris dan menghindari cek nol, inisialisasi acara Anda seperti ini: stackoverflow.com/questions/340610/…
Dinah

1
@Dinah: Itu tidak menghindari cek nol. Anda masih dapat mengaturnya SomethingHappened = nullnanti (cara yang praktis jika malas dan tidak ideal untuk berhenti berlangganan semua penangan), sehingga pemeriksaan nol selalu diperlukan.
Dan Puzey

4
@DanPuzey: Anda dapat berada di dalam kelas, tetapi Anda juga dapat memastikan bahwa Anda tidak melakukannya - dan kode lain tidak dapat melakukannya, karena hanya dapat berlangganan dan berhenti berlangganan. Jika Anda memastikan bahwa Anda tidak pernah menetapkannya ke null dengan sengaja di dalam kelas Anda, tidak apa-apa untuk menghindari cek nol.
Jon Skeet

2
@ JonSkeet: tentu saja, saya lupa Anda tidak bisa melakukan itu di luar kelas. Permintaan maaf!
Dan Puzey

2
Saya pikir Anda dapat mengganti semua barang di DoSomething denganSomethingHappened?.Invoke(this, EventArgs.Empty);
Junior Mayhé

16

Berikut ini contoh sederhana:

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

catatan:

  • Ini melanggar aturan bahwa saya tidak melepaskan pengamat dari yang bisa diamati, ini mungkin cukup baik untuk contoh sederhana ini, tetapi pastikan Anda tidak membuat pengamat menggantung acara Anda seperti itu. Cara untuk menangani ini adalah dengan membuat ObserverClass IDisposable, dan biarkan metode .Dispose melakukan kebalikan dari kode dalam konstruktor
  • Tidak ada pemeriksaan kesalahan yang dilakukan, setidaknya pemeriksaan nol harus dilakukan dalam konstruktor ObserverClass

15

Dalam model ini, Anda memiliki penerbit yang akan melakukan beberapa logika dan menerbitkan "acara."
Penerbit kemudian akan mengirimkan acara mereka hanya kepada pelanggan yang telah berlangganan untuk menerima acara tertentu.

Dalam C #, objek apa pun dapat mempublikasikan serangkaian acara yang dapat dilanggan oleh aplikasi lain.
Ketika kelas penerbitan memunculkan suatu peristiwa, semua aplikasi yang dilanggan akan diberitahukan.
Gambar berikut menunjukkan mekanisme ini.

masukkan deskripsi gambar di sini

Contoh paling sederhana yang mungkin pada Acara dan Delegasi di C #:

kode cukup jelas, Saya juga telah menambahkan komentar untuk menghapus kode.

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

Keluaran:

Dengarkan oleh Pendengar

Heard It oleh Listener2

Dengarkan oleh Pendengar

Heard It oleh Listener2

Dengarkan oleh Pendengar. . . (kali tak terbatas)


6

Saya telah menyatukan beberapa contoh hebat di atas (terima kasih seperti biasa kepada Tn. Skeet dan Tn. Karlsen ) untuk menyertakan beberapa Observable yang berbeda dan memanfaatkan antarmuka untuk melacaknya di Pengamat dan memungkinkan Pengamat untuk untuk "mengamati" sejumlah Observable melalui daftar internal:

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

Saya tahu ini sudah tua, tapi ... Ini terlihat aman, tetapi tidak. Baik di Observer.Add dan Observer.Remove cek null harus di dalam kunci. Buang juga harus mendapatkan kunci, dan atur bendera isDispised. Kalau tidak, contoh yang baik dan lengkap.
user5151179

5

Menerapkan Pola Pengamat dengan delegasi dan acara di c # dinamai "Pola Acara" menurut MSDN yang merupakan sedikit variasi.

Dalam artikel ini Anda akan menemukan contoh terstruktur dengan baik tentang bagaimana menerapkan pola dalam c # baik cara klasik dan menggunakan delegasi dan acara.

Menjelajahi Pola Desain Pengamat

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

Saya tidak ingin mengubah kode sumber saya untuk menambahkan pengamat tambahan, jadi saya telah menulis contoh sederhana berikut:

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

Sesuatu seperti ini:

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

. pengamat pola C # dengan acara . tautan ke repositori

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.