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".
using System;classObservable{publiceventEventHandlerSomethingHappened;publicvoidDoSomething()=>SomethingHappened?.Invoke(this,EventArgs.Empty);}classObserver{publicvoidHandleEvent(object sender,EventArgs args){Console.WriteLine("Something happened to "+ sender);}}classTest{staticvoidMain(){Observable observable =newObservable();Observer observer =newObserver();
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:
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);
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
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.
Contoh paling sederhana yang mungkin pada Acara dan Delegasi di C #:
kode cukup jelas, Saya juga telah menambahkan komentar untuk menghapus kode.
using System;publicclassPublisher//main publisher class which will invoke methods of all subscriber classes{publicdelegatevoidTickHandler(Publisher m,EventArgs e);//declaring a delegatepublicTickHandlerTick;//creating an object of delegatepublicEventArgs e =null;//set 2nd paramter emptypublicvoidStart()//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!}}}}publicclassSubscriber1//1st subscriber class{publicvoidSubscribe(Publisher m)//get the object of pubisher class{
m.Tick+=HeardIt;//attach listener class method to publisher class delegate object}privatevoidHeardIt(Publisher m,EventArgs e)//subscriber class method{System.Console.WriteLine("Heard It by Listener");}}publicclassSubscriber2//2nd subscriber class{publicvoidSubscribe2(Publisher m)//get the object of pubisher class{
m.Tick+=HeardIt;//attach listener class method to publisher class delegate object}privatevoidHeardIt(Publisher m,EventArgs e)//subscriber class method{System.Console.WriteLine("Heard It by Listener2");}}classTest{staticvoidMain(){Publisher m =newPublisher();//create an object of publisher class which will later be passed on subscriber classesSubscriber1 l =newSubscriber1();//create object of 1st subscriber classSubscriber2 l2 =newSubscriber2();//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}}
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;internalstaticclassProgram{privatestaticvoidMain(){var observable =newObservable();var anotherObservable =newAnotherObservable();
using (IObserver observer =newObserver(observable)){
observable.DoSomething();
observer.Add(anotherObservable);
anotherObservable.DoSomething();}Console.ReadLine();}}internalinterfaceIObservable{eventEventHandlerSomethingHappened;}internalsealedclassObservable:IObservable{publiceventEventHandlerSomethingHappened;publicvoidDoSomething(){var handler =this.SomethingHappened;Console.WriteLine("About to do something.");if(handler !=null){
handler(this,EventArgs.Empty);}}}internalsealedclassAnotherObservable:IObservable{publiceventEventHandlerSomethingHappened;publicvoidDoSomething(){var handler =this.SomethingHappened;Console.WriteLine("About to do something different.");if(handler !=null){
handler(this,EventArgs.Empty);}}}internalinterfaceIObserver:IDisposable{voidAdd(IObservable observable);voidRemove(IObservable observable);}internalsealedclassObserver:IObserver{privatereadonlyLazy<IList<IObservable>> observables =newLazy<IList<IObservable>>(()=>newList<IObservable>());publicObserver(){}publicObserver(IObservable observable):this(){this.Add(observable);}publicvoidAdd(IObservable observable){if(observable ==null){return;}lock(this.observables){this.observables.Value.Add(observable);
observable.SomethingHappened+=HandleEvent;}}publicvoidRemove(IObservable observable){if(observable ==null){return;}lock(this.observables){
observable.SomethingHappened-=HandleEvent;this.observables.Value.Remove(observable);}}publicvoidDispose(){for(var i =this.observables.Value.Count-1; i >=0; i--){this.Remove(this.observables.Value[i]);}}privatestaticvoidHandleEvent(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.
publicclassStock{//declare a delegate for the eventpublicdelegatevoidAskPriceChangedHandler(object sender,AskPriceChangedEventArgs e);//declare the event using the delegatepubliceventAskPriceChangedHandlerAskPriceChanged;//instance variable for ask priceobject _askPrice;//property for ask pricepublicobjectAskPrice{set{//set the instance variable
_askPrice =value;//fire the eventOnAskPriceChanged();}}//AskPrice property//method to fire event delegate with proper nameprotectedvoidOnAskPriceChanged(){AskPriceChanged(this,newAskPriceChangedEventArgs(_askPrice));}//AskPriceChanged}//Stock class//specialized event class for the askpricechanged eventpublicclassAskPriceChangedEventArgs:EventArgs{//instance variable to store the ask priceprivateobject _askPrice;//constructor that sets askpricepublicAskPriceChangedEventArgs(object askPrice){ _askPrice = askPrice;}//public property for the ask pricepublicobjectAskPrice{get{return _askPrice;}}}//AskPriceChangedEventArgs
/**********************Simple Example ***********************/classProgram{staticvoidMain(string[] args){Parent p =newParent();}}////////////////////////////////////////////publicdelegatevoidDelegateName(string data);classChild{publiceventDelegateName delegateName;publicvoid call(){
delegateName("Narottam");}}///////////////////////////////////////////classParent{publicParent(){Child c =newChild();
c.delegateName +=newDelegateName(print);//or like this//c.delegateName += print;
c.call();}publicvoid print(string name){Console.WriteLine("yes we got the name : "+ name);}}
Saya tidak ingin mengubah kode sumber saya untuk menambahkan pengamat tambahan, jadi saya telah menulis contoh sederhana berikut:
//EVENT DRIVEN OBSERVER PATTERNpublicclassPublisher{publicPublisher(){var observable =newObservable();
observable.PublishData("Hello World!");}}//Server will send data to this class's PublishData methodpublicclassObservable{publiceventReceiveOnReceive;publicvoidPublishData(string data){//Add all the observer below//1st observerIObserver iObserver =newObserver1();this.OnReceive+= iObserver.ReceiveData;//2nd observerIObserver iObserver2 =newObserver2();this.OnReceive+= iObserver2.ReceiveData;//publish data var handler =OnReceive;if(handler !=null){
handler(data);}}}publicinterfaceIObserver{voidReceiveData(string data);}//Observer examplepublicclassObserver1:IObserver{publicvoidReceiveData(string data){//sample observers does nothing with data :)}}publicclassObserver2:IObserver{publicvoidReceiveData(string data){//sample observers does nothing with data :)}}
SomethingHappened = null
nanti (cara yang praktis jika malas dan tidak ideal untuk berhenti berlangganan semua penangan), sehingga pemeriksaan nol selalu diperlukan.SomethingHappened?.Invoke(this, EventArgs.Empty);
Berikut ini contoh sederhana:
catatan:
sumber
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.
Contoh paling sederhana yang mungkin pada Acara dan Delegasi di C #:
kode cukup jelas, Saya juga telah menambahkan komentar untuk menghapus kode.
Keluaran:
Dengarkan oleh Pendengar
Heard It oleh Listener2
Dengarkan oleh Pendengar
Heard It oleh Listener2
Dengarkan oleh Pendengar. . . (kali tak terbatas)
sumber
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:
sumber
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
sumber
sumber
Saya tidak ingin mengubah kode sumber saya untuk menambahkan pengamat tambahan, jadi saya telah menulis contoh sederhana berikut:
sumber
Sesuatu seperti ini:
. pengamat pola C # dengan acara . tautan ke repositori
sumber