이벤트를 사용하는 코드의 설계에는 방송자구독자라는 두 가지 역할로 구성된다.

방송자(broadcaster)는 대리자 필드를 가진 대상을 말한다. 방송자는 필요할 때 대리자를 호출해서

해당 대리자는 구독하고 있는 구독자(subscribers)들에게 정보를 방송한다. 

이벤트는 이러한 패턴을 공식화하는 c#의 기능입니다.

이벤트의 주된 목적은 구독자들이 서로 간섭하지 못하게 하는 것입니다.

아래 예제를 통해서 이벤트의 간단한 사용법을 알아봅니다. 

namespace ConsoleApp1
{
    public delegate void PriceChangeHandler(decimal oldPrice, decimal newPrice);

    public class Stock
    {
        public event PriceChangeHandler PriceChanged;
        /*
         내부적으로 호출되는 형태
         public PriceChangeHandler priceChanged;
         public event PriceChangeHandler PriceChanged;
         {
            add { priceChanged += value; }
            remove { priceChanged += value; }
         }
         */
        decimal price;

        public decimal Price
        {
            get { return price; }
            set
            {
                if (price == value) return;
                decimal oldPrice = price;
                price = value;
                if(PriceChanged != null)
                {
                    // price가 변하면 구독자들에게 알립니다.
                    PriceChanged(oldPrice, price);
                }
            }
        }
    }

    class Test
    {
        static public void NoticePriceChange(decimal old, decimal newP)
        {
            Console.WriteLine("가격 변화 " + old + " -> " + newP);
        }
           

        public static void Main(String[] args)
        {
            Stock s =new Stock();
            s.Price = 1000;
            s.PriceChanged += NoticePriceChange;
            s.Price = 2000;
        }
    }

}

결과

예제를 보면 "public event PriceChangeHandler PriceChanged" 라는 이벤트 객체를 선언해서 구독자들이

등록할 수 있도록 합니다. 

"Price" 값이 변경될 때 PriceChanged를 호출하면서 OldPrice와 NewPrice를 구독자들에게 전달하도록 되어 있습니다. 

예제에서 구독자는 NoticePriceChange 함수를 등록해서 변경된 데이터를 출력했습니다. 

구독자들은 해당 전달 값을 받아서 필요한 작업을 진행합니다.

 

표준 이벤트 패턴

c++을 사용하다 c#을 사용하게 되면 느끼는 것이 표준화된 양식들을 다양하게 제공한다는 점입니다. 

표준 이벤트 패턴도 이러한 표준화된 양식 중에 하나입니다. 

첫 번째로 살펴볼 클래스는 System.EventArgs입니다. 이 클래스는 아무런 멤버도 없습니다. 

이 클래스의 역할은 이벤트 발생 시 구독자들에게 전달되는 정보를 구현하기 위한 기반 클래스입니다.

위의 예제의 PriceChangeHandler의 전달 인수를 System.EventArgs로 구현해 봅시다.

    public class PriceChangedEventArgs : System.EventArgs
    {
        public readonly decimal LastPrice;
        public readonly decimal NewPrice;

        public PriceChangedEventArgs(decimal lastPrice, decimal newPrice)
        {
            LastPrice = lastPrice;
            NewPrice = newPrice;
        }
    }

이제 Price가 변경되면 PriceChangedEventArgs에 OldPrice와 NewPrice값을 담아서 전달하면

깔끔하게 전달이 가능합니다. 

모두 System.EventArgs상속받았기 때문에 공통된 인터페이스로 구현할 수 있습니다. 

이벤트를 위한 전달 인자에 대한 정의를 끝마쳤으니 두 번째로 살펴볼 대상은 System.EventHandler<> 입니다. 

public event EventHandler<PriceChangedEventArgs> PriceChanged;

이벤트를 위한 대리자의 역할을 진행합니다. 형식의 조건은 아래와 같습니다.

  • 반환 형식이 반드시 void이어야 한다.
  • 인수를 두개 받아야 한다. 첫 인수의 형식은 object이고 둘째 형식은 EventArgs의 파생 클래스이다.                      ( 첫인수는 이벤트 방송자 객체, 두번째 인수는 이벤트 전달 정보입니다.)
  • 이름이 반드시 EventHandler로 끝나야한다. 

표준 이벤트 패턴에 따르려면 이벤트를 발동하는 가상 메서드를 하나 작성해야 합니다. 

protected virtual void OnPriceChanged (PriceChangedEventArgs e)
{
    PriceChanged?.Invoke(this, e);
}
        

 

최종적으로 표준 이벤트 패턴을 따르는 예제를 작성해봅니다. 

namespace ConsoleApp1
{
    public class PriceChangedEventArgs : System.EventArgs
    {
        public readonly decimal LastPrice;
        public readonly decimal NewPrice;

        public PriceChangedEventArgs(decimal lastPrice, decimal newPrice)
        {
            LastPrice = lastPrice;
            NewPrice = newPrice;
        }
    }

    public class Stock
    {
        decimal price;

        public event EventHandler<PriceChangedEventArgs> PriceChanged;

        protected virtual void OnPriceChanged (PriceChangedEventArgs e)
        {
            PriceChanged?.Invoke(this, e);
        }
        

        public decimal Price
        {
            get { return price; }
            set
            {
                if (price == value) return;
                decimal oldPrice = price;
                price = value;
                if(PriceChanged != null)
                {
                    // price가 변하면 구독자들에게 알립니다.
                    OnPriceChanged(new PriceChangedEventArgs(oldPrice, price));
                }
            }
        }
    }

    class Test
    {
        static public void NoticePriceChanged(object sender, PriceChangedEventArgs e)
        {
            Console.WriteLine("가격 변화 " + e.LastPrice + " -> " + e.NewPrice);
        }
           

        public static void Main(String[] args)
        {
            Stock s =new Stock();
            s.Price = 1000;
            s.PriceChanged += NoticePriceChanged;
            s.Price = 2000;
        }
    }

}

PriceChanged라는 EventHandler객체를 등록한 후 OnPriceChanged() 함수를 통해서 Price 값 변경시

PriceChangedEventArgs인자와 Sender를 전달해서 구독자들이 사용할 수 있도록 제공해줍니다.

또한 인터페이스도 표준적으로 제공해서 표준이벤트를 공부한 사람들이 서로 이해하기 쉬운 코드로 제공되는 점이 

장점입니다. 

이렇게 표준 스타일을 제공해서 인터페이스의 통일화를 제공하는 c#은 개인적으로 깔끔해서 좋습니다. 

'c#' 카테고리의 다른 글

[c#] 람다 표현식  (0) 2019.09.27
[c#] 대리자(delegate)  (0) 2019.09.23

+ Recent posts