델리게이트로 C # 옵저버 / 관측 가능한 매우 간단한 예


131

나는 최근에 C #을 파기 시작했지만 인생에서 관찰자 / 관찰 가능한 패턴을 언어로 구현할 때 델리게이트가 어떻게 작동하는지 알 수 없습니다.

누군가 나에게 그것이 어떻게 수행되는지에 대한 아주 간단한 예를 줄 수 있습니까? 나는 이 봤지만, 내가 찾은 모든 예제 중 하나를 너무 문제가 특정거나 "비 대한"이었다.

답변:


218

관찰자 패턴은 일반적으로 이벤트로 구현됩니다. .

예를 들면 다음과 같습니다.

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

자세한 내용은 링크 된 기사를 참조하십시오.

위의 예는 C # 6 null 조건부 연산자를 사용하여 구독하지 않은 DoSomething경우를 처리하기 위해 안전하게 구현 SomethingHappened하므로 null입니다. 이전 버전의 C #을 사용하는 경우 다음과 같은 코드가 필요합니다.

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

17
자신에게 몇 줄을 저장하고 널 체크를 방지하려면 초기화이 같은 이벤트 : stackoverflow.com/questions/340610/...
디나

1
@ Dinah : null 확인을 피하지 않습니다. SomethingHappened = null나중에 설정할 수 있습니다 (모든 핸들러를 구독 취소하는 게으르고 비 이상적 인 경우 편리함). 항체 검사는 항상 필요합니다.
Dan Puzey

4
@ DanPuzey : 클래스 내에서 할 수는 있지만 똑같이 그렇게하지 않도록 할 수 있습니다- 다른 코드는 구독하거나 구독을 취소 할 수 있기 때문에 그렇게 할 수 없습니다. 클래스 내에서 의도적으로 null로 설정하지 않은 경우 null 확인을 피하는 것이 좋습니다.
Jon Skeet

2
@ JonSkeet : 물론, 나는 당신이 수업 밖에서 그것을 할 수 없다는 것을 잊고있었습니다. 사과!
Dan Puzey

2
DoSomething의 모든 내용을SomethingHappened?.Invoke(this, EventArgs.Empty);
Junior Mayhé

16

다음은 간단한 예입니다.

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 메소드가 생성자의 코드와 반대로 작동하게하는 것입니다.
  • 오류 검사가 수행되지 않으며 ObserverClass의 생성자에서 최소한 null 검사가 수행되어야합니다.

15

이 모델에는 논리를 수행하고 "이벤트"를 게시 할 게시자가 있습니다.
그런 다음 게시자는 특정 이벤트를 받도록 구독 한 구독자에게만 이벤트를 보냅니다.

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에 의해 들었습니다

청취자에게 들었습니다. . . (무한시)


6

위의 몇 가지 훌륭한 예를 함께 묶어 두었 습니다 (SkeetMr. Karlsen 에게 항상 감사합니다 ). 다른 Observable을 포함시키고 Observer에서 추적 할 수있는 인터페이스를 사용했습니다. 내부 목록을 통해 다수의 Observable을 "관찰"

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

나는 이것이 오래되었다는 것을 알고 있지만 ... 이것은 스레드가 안전 해 보이지만 그렇지 않습니다. Observer.Add 및 Observer.Remove 모두에서 널 검사는 잠금 내부에 있어야합니다. Dispose는 잠금을 요구하고 isDispised 플래그를 설정해야합니다. 그렇지 않으면 좋은 완전한 예입니다.
user5151179

5

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

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

추가 관찰자를 추가하기 위해 소스 코드를 변경하고 싶지 않았으므로 다음 간단한 예제를 작성했습니다.

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

이 같은:

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

. 이벤트와 관찰자 패턴 C 번호 . 저장소에 연결

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.