블로그 이미지
대갈장군

calendar

1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

Notice

2010. 6. 25. 02:37 프로그래밍/C#
이벤트, 이벤트 핸들러. 이 두 단어는 API와 C++을 공부한 나에게는 아주 익숙하게 들린다. C#이 아닌 다른 언어에서는 이벤트란 API에서 비동기적으로 특정한 호출을 일으키는 명령을 의미하고 이벤트 헨들러는 그것을 받는 핸들러를 의미한다라고 아주 자연스럽게 이해가 된다.

하지만 C#에서 이벤트와 이벤트 핸들러는 좀 다른 의미다. 겹쳐지는 의미가 있기는 한데 바로 '비동기적 호출'이라는 점이다. C#에서 이벤트는 호출되어야 하는 함수의 목록을 의미한다. 중요한 것은 '목록'이라는 건데 이건 다수가 될 수 있다는 점이다. 즉, 멀티캐스트라는 것...

즉, 이벤트는 멀티캐스트 델리게이트로 구현되며 호출되어야 하는 함수 목록을 순차적으로 가진다... 그리고 이벤트에 의해 호출되는 함수를 '이벤트 핸들러'라고 한다. 이벤트를 핸들 하는 놈이라는 의미에서 이벤트 핸들러라 하는 갑다.

사실 C에 보면 메시지를 통해 이런 것을 구현했었다. 메시지를 특정 윈도우에 보내면 해당 윈도우의 메시지 처리 함수에서 메시지에 해당하는 함수를 구현하는 그런 방식이었다. 하지만 이것은 일단 복잡하고 비-객체 지향적이란다. 뭐, 객체 지향이네 아니네는 코딩을 어떻게 하느냐의 차이지만 C에서는 아무래도 언어 자체적인 객체 지향성이 C#에 비해 떨어진다는 의미다.

아무튼 이런 C의 메시지를 객체 지향적으로 포장한 것이 C#의 이벤트라는 것이다. 이벤트는 다음과 같이 선언한다.

엑세스지정자 event 델리게이트타입 이름;

이 위에 선언식을 보아하니 델리게이트와 좀 헷갈리지 않는가? 델리게이트의 선언은 다음과 같다.

엑세스지정자 delegate 리턴타입 이름(인수목록);

event를 선언하기 위해 사용된 델리게이트타입이라는 놈은 바로 델리게이트 타입 선언에 의해 선언된 놈이다. 즉, 예를 들면,

public delegate void MyDelegate(int a, int b);
public event MyDelegate
PrintOutEverything
MyEvent;

위와 같이 사용된다는 것... 델리게이트와 델리게이트 타입. 이 두 놈이 조금 복잡한듯 하나 위에 놈은 델리게이트의 타입 선언문이고 아래는 선언된 델리게이트 타입을 사용한다는 차이만 알면 될듯.

또 한가지 중요한 것은 이벤트 핸들러 즉, 호출 되는 함수는 리턴값을 가지지 않는다는 것. 원래 이벤트라는 것이 알려주기 위함이지 얻어오기 위함이 아니기 때문이다. 더군다나 이벤트 핸들러는 비동기적으로 호출되므로 리턴한다고 해도 받아 줄 놈이 없어졌을 수도 있다. 

그런데 자세히 보면 이벤트라는 놈은 사실 델리게이트타입을 재정의 한 것일뿐 특별한 차이점이 델리게이트와 비교해서 없다. 즉, 이벤트라는 지시자가 없더라도 델리게이트만으로도 모든 기능을 구현할 수 있다는 말이다. 사실 나도 이 부분을 MSDN 책을 보면서 '왜 두번 거쳐서 복잡하게 처리할까?' 라고 생각했던 부분인데 김상형님의 책에서 시원하게 설명해 주고 있다. 

이벤트는 델리게이트와 다르게 인수 정보를 사용자에게 보여주지 않고 감추며 멀티스레드에 안전하다는 장점이 있다. 즉, 사용자에게 보다 쉽고 안전한 방식으로 델리게이트를 사용하도록 했다는 것. 지금 보기에는 이것이 더 복잡해 보일지 모르나 비쥬얼 스튜디오의 마법사를 이용하면 이 과정이 사용자에게는 모두 숨겨지고 단 한번의 더블클릭으로 이루어 진다. 

그렇다면 실제로 이벤트를 폼에 적용하는 예를 살펴보자.

C#의 프로젝트를 폼 형태로 생성하면 메인 클래스는 반드시 Form을 상속받게 되어 있다. 이 Form 클래스에는 Paint라는 이벤트가 멤버로 들어가 있는데 바로 다음과 같이 선언되어 있다.

public event PaintEventHandler Paint;

여기서 PaintEventHandler가 바로 델리게이트 타입이다. 이 델리게이트 타입의 선언을 따라가보면 다음과 같이 선언 되어 있다.

public delegate void PaintEventHandler(Object sender, PaintEventArgs e);

보시다시피 리턴값이 없는 델리게이트 선언문이다. 고로 사용자는 위의 인수를 가지는 함수를 생성하여 Paint 이벤트에 대입만 해주면 화면 그리기 명령을 손쉽게 수행할 수 있는 것이다. 

이 방법 외에 윈도우에 그림을 그리는 방법이 한가지 더 있는데 바로 OnPaint 가상 함수를 재정의 하는 것이다. 이 경우에는 Control (부모)에 정의 되어 있는 OnPaint() 함수를 오버라이드 하는 것이다. 이렇게 하면 이벤트 핸들러를 사용할 필요가 없지. 

그렇다면 이 둘의 차이는? 만약 이 둘다 정의해 놓고 호출해 보면 오버라이딩 된 함수가 먼저 호출된다. 즉, OnPaint() 가상함수가 우선순위가 높다는 것이다.

헌데 막상 비주얼 스튜디오에서 더블클릭해서 이벤트 추가를 해보면 오버라이딩을 하지 않고 이벤트 핸들러를 사용하는 것을 알수 있다. 그 이유는 상속을 안받아도 되기 때문. 가상 함수를 오버라이딩 시킬려면 해당 함수를 가진 부모를 상속받아야 하는 불편함이 있다. 그에 비해 이벤트 핸들러는 델리게이트를 이용하므로 타입과 인수목록만 맞으면 자식 부모 관계 없이 호출 가능하다는 장점이 있다. 고로 이벤트 핸들러의 승리!

1. 이벤트의 정의는?
2. 이벤트 핸들러는 리턴값을 가지지 않는데 왜 그럴까?
3. 델리게이트와 이벤트는 어떻게 다른가?
4. 왜 Visual Studio에서 마법사로 함수를 추가하면 가상함수 오버라이딩 대신 이벤트 핸들러를 사용하는가?


'프로그래밍 > C#' 카테고리의 다른 글

Asynchronous programming  (0) 2017.05.31
C# - 제너릭 (Generic) 에 대한 질문  (0) 2010.06.18
Covariance and Contravariance  (0) 2010.06.18
델리게이트 - Delegate  (0) 2010.06.17
posted by 대갈장군