2022. 1. 18. 00:00ㆍCSharp/Advance
클래스나 개체에서 특정 상황이 발생할 때 이벤트를 통해 다른 클래스나 개체에 이를 알려 줄 수 있다.
이벤트를 보내거나 발생시키는 클래스를 게시자 (publisher)
이벤트를 받거나 처리하는(handle) 클래스를 구독자 (subscribers)
이벤트는 선언된 클래스 내에서만 호출할 수 있는 특수한 종류의 멀티캐스트 대리자이다.
Dotnet 에서 표준적으로 이벤트를 게시하는 방법이 EventHandler 를 이용하는 방법이므로 이를 이용하자
아래와 같은 방식으로 선언
public event EventHandler<CustomEventArgs> RaiseCustomEvent;
원래 delegate 로 표현하면 아래와 같다고 생각하면 된다.
public delegate void CustomEventHandler(object sender, CustomEventArgs args);
예제)
public class StandardEvents
{
public class CustomEventArgs : EventArgs
{
public CustomEventArgs(string message)
{
Message = message;
}
public string Message { get; set; }
}
// Class that publishes an event
public class Publisher
{
// Declare the event using EventHandler<T>
public event EventHandler<CustomEventArgs> RaiseStandardEvent;
public void FiredStandardEvent()
{
// Write some code that does something useful here
// then raise the event. You can also raise an event
// before you execute a block of code.
OnRaiseCustomEvent(new CustomEventArgs("표준 이벤트 발생"));
}
// Wrap event invocations inside a protected virtual method
// to allow derived classes to override the event invocation behavior
protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> raiseEvent = RaiseStandardEvent;
// Event will be null if there are no subscribers
if (raiseEvent != null)
{
// Format the string to send inside the CustomEventArgs parameter
e.Message += $" at {DateTime.Now}";
// Call to raise the event.
raiseEvent(this, e);
}
}
}
//Class that subscribes to an event
public class Subscriber
{
private readonly string _id;
public Subscriber(string id, Publisher pub)
{
_id = id;
// Subscribe to the event
pub.RaiseStandardEvent += OnRaiseStandardEvent;
}
// Define what actions to take when the event is raised.
private void OnRaiseStandardEvent(object sender, CustomEventArgs e)
{
Console.WriteLine($"{_id} received this message: {e.Message}");
}
}
public class Test : ITest
{
public void Run()
{
var pub = new Publisher();
var sub1 = new Subscriber("sub1", pub);
//var sub2 = new Subscriber("sub2", pub);
// Call the method that raises the event.
pub.FiredStandardEvent();
}
}
}
이벤트 등록
Add (+=)
pub.RaiseCustomEvent += HandleCustomEvent;
이벤트 삭제
Remove (-=)
pub.RaiseCustomEvent -= HandleCustomEvent;
*** delegate 와의 차이 ***
1. event 는 정의한 class 내부에서만(상속한 class 에서도 불가) event 를 trigger 할 수 있다.
delegate 는 정의한 class 뿐만 아니라 외부에서도 delegate 를 trigger 할 수 있다.
2. event 는 interface 에 정의 할 수 있다.
delegate type 으로 정의된 변수를 interface 에서 사용하려면 property 형태로 만들어야한다.
event 는 단순한 수정자 이다.
delegate 같은 경우 치명적인 문제점(?) 이 있는데 바로 delegate type 변수를 생성한 class 외부에서
delegate 자체를 trigger 할 수 있다는 것이다. 어떤 말이냐 하면 아래 코드를 보자
public class DelegatesVsEvents
{
// 비표준 형태의 event type 선언
public delegate void NonStandardEventType(string message);
// Class that publishes an event
public class Publisher
{
// NonStandardEventType 의 event 수정자를 통해 delegate 변수 생성
public event NonStandardEventType RaiseEvent;
// NonStandardEventType 의 delegate 변수 생성
public NonStandardEventType RaiseDelegate;
public void FiredRaiseEvent()
{
RaiseEvent("이벤트 발생");
}
public void FiredRaiseDelegate()
{
RaiseEvent("delegate 발생");
}
}
//Class that subscribes to an event
public class Subscriber
{
private readonly string _id;
public Subscriber(string id, Publisher pub)
{
_id = id;
// Subscribe to the event
pub.RaiseEvent += OnRaiseEvent;
// Subscribe to the delegate
pub.RaiseDelegate += OnRaiseDelegate;
// 기본적으로 위 두가지는 같다.
// 하지만
//pub.RaiseEvent("내가 훔쳤다 ㅋㅋㅋㅋ"); // 컴파일 되지 않는다.
pub.RaiseDelegate("내가 훔쳤다 ㅋㅋㅋㅋ"); // 가능하다.
}
private void OnRaiseEvent(string message)
{
message += $" at {DateTime.Now}";
Console.WriteLine($"OnRaiseEvent {_id} received this message: {message}");
}
private void OnRaiseDelegate(string message)
{
message += $" at {DateTime.Now}";
Console.WriteLine($"OnRaiseDelegate {_id} received this message: {message}");
}
}
public class Test : ITest
{
public void Run()
{
var pub = new Publisher();
var sub1 = new Subscriber("sub1", pub);
//var sub2 = new Subscriber("sub2", pub);
// event 발생;
pub.FiredRaiseEvent();
// delegate 발생;
pub.FiredRaiseEvent();
}
}
}
즉 아래 코드 두부분이 event 와 delegate 의 가장 큰 차이점이라고 할 수 있다.
//pub.RaiseEvent("내가 훔쳤다 ㅋㅋㅋㅋ"); // 컴파일 되지 않는다.
pub.RaiseDelegate("내가 훔쳤다 ㅋㅋㅋㅋ"); // 가능하다.
event 는 event 자체를 외부에서 trigger 할 수 없다. 내부에서만 가능하다. 이게 event 수정자의 역할이다.
event 는 interface 상에 정의 할 수 있다.
delegate 는 단순 field 로 인식 하므로 interface 상에 정의 하려면 property 형태여야 한다.
delegate type 선언 이란 의미는 아래와 같다.
// Delegate type 선언
public delegate Shape CreateShapeDelegate();
public delegate void AddCircleDelegate(Circle e);
public delegate void AddRectangleDelegate(Rectangle e);
public delegate void AddTriangleDelegate(Triangle e);
선언된 delegate type 을 이용하여 delegate 변수를 정의 한다는 의미는 아래와 같다.
// 선언된 deletegate type 을 이용하여 변수 정의
public CreateShapeDelegate? MultiAction;
public AddCircleDelegate? AddCircleDelegateHandler;
public AddRectangleDelegate? AddRectangleDelegateHandler;
public AddTriangleDelegate? AddTriangleDelegateHandler;
그렇다면 event 에서 우리가 원하는 특정 delegate type 을 사용하여 event 를 fire 해보자
public class NonStandardEvents
{
// 비표준 형태의 event type 선언
public delegate void NonStandardEventType(string message);
// Class that publishes an event
public class Publisher
{
// NonStandardEventType 의 event 변수 생성
public event NonStandardEventType RaiseNonStandardEvent;
public void FiredNonStandardEvent()
{
RaiseNonStandardEvent("비표준 이벤트 발생");
}
}
//Class that subscribes to an event
public class Subscriber
{
private readonly string _id;
public Subscriber(string id, Publisher pub)
{
_id = id;
// Subscribe to the event
pub.RaiseNonStandardEvent += OnRaiseNonStandardEvent;
}
private void OnRaiseNonStandardEvent(string message)
{
message += $" at {DateTime.Now}";
Console.WriteLine($"{_id} received this message: {message}");
}
}
public class Test : ITest
{
public void Run()
{
var pub = new Publisher();
var sub1 = new Subscriber("sub1", pub);
//var sub2 = new Subscriber("sub2", pub);
// 비표준 event 실행;
pub.FiredNonStandardEvent();
}
}
}
관련영상
'CSharp > Advance' 카테고리의 다른 글
제네릭 컬렉션 (Generic Collection) (0) | 2022.01.24 |
---|---|
Record (0) | 2022.01.21 |
Lambda advanced (0) | 2022.01.20 |
Action, Func, Lambda (0) | 2022.01.19 |
Delegate (대리자) (0) | 2022.01.17 |