C # gözlemcisinin süper basit örneği / delegelerle gözlemlenebilir


131

Kısa bir süre önce C # 'ı araştırmaya başladım, ancak dilde gözlemci / gözlemlenebilir modeli uygularken delegelerin nasıl çalıştığını hayatımdan anlayamıyorum.

Birisi bana bunun nasıl yapıldığına dair süper basit bir örnek verebilir mi? Ben var bu googled ama bulundu örnekler tüm ya da problem kesin veya çok "şişirilmiş" idi.

Yanıtlar:


218

Gözlemci modeli genellikle olaylarla uygulanır .

İşte bir örnek:

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

Daha fazla ayrıntı için bağlantılı makaleye bakın.

Yukarıdaki örneğin, abone olunmamış durumları güvenli bir şekilde ele almak için C # 6 boş koşullu operatör kullandığını ve bu nedenle boş olduğunu unutmayın. Daha eski bir C # sürümü kullanıyorsanız, bunun gibi bir koda ihtiyacınız olacaktır:DoSomethingSomethingHappened

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

17
Kendinizi birkaç satırdan kurtarmak ve sıfır kontrolünden kaçınmak için etkinliğinizi şu şekilde başlatın: stackoverflow.com/questions/340610/…
Dinah

1
@Dinah: Bu, boş kontrolden kaçınmaz. Yine de SomethingHappened = nulldaha sonra ayarlayabilirsiniz (tembel olsa da kullanışlı ve tüm işleyicilerin aboneliğini iptal etmenin ideal olmayan bir yolu), böylece boş kontrol her zaman gereklidir.
Dan Puzey

4
@DanPuzey: Sınıfın içinde can, ama aynı derecede sen emin olabiliriz yok bunu - ve diğer yalnızca abone ve abonelikten gibi kod, bunu yapamaz. Sınıfınız içinde asla kasıtlı olarak null değerine ayarlamadığınızdan emin olursanız, sıfır kontrolünden kaçınmakta sorun yoktur.
Jon Skeet

2
@JonSkeet: Tabii ki bunu sınıf dışında yapamayacağını unutuyordum. Özür!
Dan Puzey

2
Sanırım DoSomething'deki her şeyiSomethingHappened?.Invoke(this, EventArgs.Empty);
Junior Mayhé

16

İşte basit bir örnek:

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

Not:

  • Bu, gözlemciyi gözlemlenebilir olandan ayırmadığım için bir kuralı ihlal ediyor, bu belki bu basit örnek için yeterince iyi, ancak gözlemcileri olaylarınızdan bu şekilde sarkıtmaya devam etmediğinizden emin olun. Bunu halletmenin bir yolu, ObserverClass IDisposable yapmak ve .Dispose yönteminin yapıcıdaki kodun tersini yapmasına izin vermek olacaktır.
  • Hiçbir hata kontrolü yapılmadı, en azından ObserverClass yapıcısında bir boş kontrol yapılmalıdır.

15

Bu modelde, biraz mantık yapacak ve bir "etkinlik" yayınlayan yayıncılarınız var.
Yayıncılar daha sonra etkinliklerini yalnızca belirli etkinliği almak için abone olan abonelere gönderir.

C # 'da herhangi bir nesne, diğer uygulamaların abone olabileceği bir dizi olay yayınlayabilir.
Yayın sınıfı bir olay ortaya çıkardığında, abone olunan tüm uygulamalar bilgilendirilir.
Aşağıdaki şekil bu mekanizmayı göstermektedir.

görüntü açıklamasını buraya girin

C # 'taki Olaylar ve Temsilciler için Olası En Basit Örnek:

kod kendinden açıklamalıdır, Ayrıca kodu temizlemek için yorumları ekledim.

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

Çıktı:

Dinleyici tarafından duydum

Dinleyici2 tarafından duydum

Dinleyici tarafından duydum

Dinleyici2 tarafından duydum

Dinleyici tarafından duydum. . . (sonsuz kez)


6

Yukarıdaki harika örneklerden birkaçını ( Bay Skeet ve Bay Karlsen'e her zaman olduğu gibi ) birkaç farklı Gözlemlenebilir Öğeyi dahil etmek için bir araya getirdim ve onları Gözlemci'de takip etmek için bir arayüz kullandım ve Gözlemcinin dahili bir liste aracılığıyla herhangi bir sayıda Gözlenebilir Öğeyi "gözlemlemek" için:

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

Bunun eski olduğunu biliyorum, ama ... Bu iş parçacığı güvenli görünüyor, ama değil. Hem Observer.Add hem de Observer.Remove'da boş kontrolün kilidin içinde olması gerekir. Dispose ayrıca kilidi almalı ve bir isDispised bayrağı ayarlamalıdır. Aksi takdirde iyi ve eksiksiz bir örnek.
user5151179

5

Uygulama Gözlemci Desen delege ve olaylarla c # adlandırılır "Olay Desen" göre MSDN hafif çeşididir.

Bu Makalede, kalıbı hem klasik yolla hem de delegeleri ve olayları kullanarak c # 'da nasıl uygulayacağınıza dair iyi yapılandırılmış örnekler bulacaksınız.

Gözlemci Tasarım Modelini Keşfetmek

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

Ek gözlemci eklemek için kaynak kodumu değiştirmek istemedim, bu yüzden aşağıdaki basit örneği yazdım:

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

Bunun gibi bir şey:

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

. olaylı gözlemci deseni C # . arşive bağlantı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.