Супер-простой пример c# observer / observable с делегатами
недавно я начал копаться в C#, но я не могу по своей жизни понять, как работают делегаты при реализации шаблона observer/observable на языке.
может кто-нибудь дать мне супер-простой пример, как это делается? Я есть погуглил это, но все примеры, которые я нашел, были либо слишком специфичными для проблемы, либо слишком "раздутыми".
9 ответов:
шаблон наблюдателя обычно реализуется с помощью событий.
вот пример:
using System; class Observable { public event EventHandler SomethingHappened; public void DoSomething() { EventHandler handler = SomethingHappened; if (handler != null) { handler(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(); } }
см. связанную Статью для получения более подробной информации.
вот простой пример:
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; } }
Примечание:
- это нарушает правило в том, что я не отцепляю наблюдателя от наблюдаемого, это, возможно, достаточно хорошо для этого простого примера, но убедитесь, что вы не держите наблюдателей, зависающих от ваших событий. Способ справиться с этим-сделать ObserverClass IDisposable, и пусть .Метод Dispose делает противоположное коду в конструкторе
- проверка ошибок не выполняется, по крайней мере a null-проверка должна быть выполнена в конструкторе ObserverClass
в этой модели у вас есть издатели, которые будут делать некоторую логику и публиковать "событие."
Затем издатели будут отправлять свое событие только подписчикам, которые подписались на получение конкретного события.в C# любой объект может публиковать набор событий, на которые могут подписаться другие приложения.
Когда класс публикации вызывает событие, уведомляются все подписанные приложения.
Это показано на следующем рисунке механизм.самый простой пример, возможный для событий и делегатов в C#:
код не требует пояснений, также я добавил комментарии, чтобы снять код.
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 } }
выход:
услышал его слушатель
услышал его Listener2
услышал его слушатель
услышал его Listener2
слышали об этом Слушатель . . . (бесконечное время)
я связал вместе несколько замечательных примеров выше (спасибо как всегда Г-Н Тарелочкам и Г-Н Карлсен) включить несколько различных наблюдаемых объектов и использовать интерфейс для отслеживания их в наблюдателе и позволить наблюдателю "наблюдать" любое количество наблюдаемых объектов через внутренний список:
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); } } }
проверьте это введение в Rx Framework, который использует замечательный IObserver-IObservable неблокирующая асинхронная модель программирования введение-rx-linq-to-events
применение Шаблон Observer с делегатами и событиями в c# называется "Шаблон Событий" по данным MSDN что является небольшим изменением.
в этой статье вы найдете хорошо структурированные примеры применения шаблона в c# как классическим способом, так и с использованием делегатов и событий.
изучение шаблона проектирования наблюдателя
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
/**********************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); } }
Я не хотел менять свой исходный код, чтобы добавить дополнительный наблюдатель, поэтому я написал следующий простой пример:
//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 :) } }
что-то вроде этого:
// 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) { } }