Ví dụ siêu đơn giản về người quan sát C # / có thể quan sát được với các đại biểu


131

Gần đây tôi đã bắt đầu đào sâu vào C # nhưng trong đời tôi không thể tìm ra cách các đại biểu làm việc khi thực hiện mô hình quan sát / quan sát được bằng ngôn ngữ.

Ai đó có thể cho tôi một ví dụ siêu đơn giản về cách nó được thực hiện? Tôi đã googled điều này, nhưng tất cả các ví dụ tôi tìm thấy là quá cụ thể hoặc quá "cồng kềnh".

Câu trả lời:


218

Mẫu quan sát thường được thực hiện với các sự kiện .

Đây là một ví dụ:

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

Xem bài viết liên kết để biết thêm chi tiết.

Lưu ý rằng ví dụ trên sử dụng toán tử có điều kiện null C # 6 để triển khai DoSomethingan toàn để xử lý các trường hợp SomethingHappenedchưa được đăng ký và do đó là null. Nếu bạn đang sử dụng phiên bản C # cũ hơn, bạn sẽ cần mã như thế này:

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

17
Để tiết kiệm cho mình một vài dòng và tránh kiểm tra null, hãy khởi tạo sự kiện của bạn như thế này: stackoverflow.com/questions/340610/
Kẻ

1
@Dinah: Điều đó không tránh được kiểm tra null. Bạn vẫn có thể thiết lập SomethingHappened = nullsau (một cách tiện dụng nếu lười biếng và không lý tưởng để hủy đăng ký tất cả các trình xử lý), vì vậy kiểm tra null luôn luôn cần thiết.
Dan Puzey

4
@DanPuzey: Bạn có thể trong lớp, nhưng bạn cũng có thể chắc chắn rằng mình không làm điều đó - và cáckhác không thể làm điều đó, vì nó chỉ có thể đăng ký và hủy đăng ký. Nếu bạn đảm bảo rằng bạn không bao giờ đặt nó thành null một cách có chủ ý trong lớp của bạn, thì tốt nhất là tránh kiểm tra null.
Jon Skeet

2
@JonSkeet: tất nhiên, tôi đã quên bạn không thể làm điều đó ngoài lớp học. Xin lỗi!
Dan Puzey

2
Tôi nghĩ bạn có thể thay thế tất cả mọi thứ trong SomethingHappened?.Invoke(this, EventArgs.Empty);
DoS Something

16

Đây là một ví dụ đơn giản:

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

Ghi chú:

  • Điều này vi phạm một quy tắc ở chỗ tôi không bỏ qua người quan sát khỏi điều có thể quan sát được, điều này có lẽ đủ tốt cho ví dụ đơn giản này, nhưng hãy chắc chắn rằng bạn không để các nhà quan sát tránh xa các sự kiện của bạn như thế. Một cách để xử lý việc này sẽ là tạo ra ObserverClass IDis Dùng một lần và để phương thức .Dispose làm ngược lại với mã trong hàm tạo
  • Không có kiểm tra lỗi được thực hiện, ít nhất phải thực hiện kiểm tra null trong hàm tạo của ObserverClass

15

Trong mô hình này, bạn có các nhà xuất bản sẽ thực hiện một số logic và xuất bản một "sự kiện".
Các nhà xuất bản sau đó sẽ chỉ gửi sự kiện của họ đến những người đăng ký đã đăng ký để nhận sự kiện cụ thể.

Trong C #, bất kỳ đối tượng nào cũng có thể xuất bản một tập hợp các sự kiện mà các ứng dụng khác có thể đăng ký.
Khi lớp xuất bản tăng sự kiện, tất cả các ứng dụng đã đăng ký sẽ được thông báo.
Hình dưới đây cho thấy cơ chế này.

nhập mô tả hình ảnh ở đây

Ví dụ đơn giản nhất có thể về các Sự kiện và Đại biểu trong C #:

mã là tự giải thích, Ngoài ra tôi đã thêm các ý kiến ​​để xóa mã.

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

Đầu ra:

Nghe nó bởi người nghe

Nghe nó bởi Listener2

Nghe nó bởi người nghe

Nghe nó bởi Listener2

Nghe nó bởi Người nghe. . . (thời gian vô hạn)


6

Tôi đã liên kết với nhau một vài ví dụ tuyệt vời ở trên (cảm ơn bạn luôn luôn là ông Skeetông Karlsen ) để bao gồm một vài Đài quan sát khác nhau và sử dụng một giao diện để theo dõi chúng trong Người quan sát và cho phép Người quan sát để "quan sát" bất kỳ số lượng Đài quan sát nào thông qua danh sách nội bộ:

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

Tôi biết cái này đã cũ, nhưng ... Cái này có vẻ an toàn, nhưng không phải. Trong cả Observer.Add và Observer. Hãy kiểm tra null cần nằm trong khóa. Vứt bỏ cũng nên lấy khóa và đặt cờ isDispised. Nếu không, một ví dụ tốt, đầy đủ.
dùng5151179

5

Áp dụng Mẫu quan sát với các đại biểu và sự kiện trong c # được đặt tên là "Mẫu sự kiện" theo MSDN , đây là một biến thể nhỏ.

Trong bài viết này, bạn sẽ tìm thấy các ví dụ có cấu trúc tốt về cách áp dụng mẫu trong c # theo cách cổ điển và sử dụng các đại biểu và sự kiện.

Khám phá mẫu thiết kế quan sát

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

Tôi không muốn thay đổi mã nguồn của mình để thêm người quan sát, vì vậy tôi đã viết ví dụ đơn giản sau:

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

Một cái gì đó như thế này:

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

. mẫu quan sát C # với sự kiện . liên kết đến kho lưu trữ

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.