이벤트를 사용하는 코드의 설계에는 방송자와 구독자라는 두 가지 역할로 구성된다.
방송자(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 |