삽질의 현장/- .NET

#053_닷넷(.NET)_.Net Framework 기본 - 이벤트(Event)

shovelman 2015. 11. 3. 20:26


안녕하세요 삽잡이입니다.


이번 시간에는 delegate에 이어 event에 대해서 알아보려고합니다.


delegate는 목적이 Callback인지 아닌지를 구분해야하니 헷갈리는 경우가 있습니다.

즉, delegate는 여러 관점에서 쓰이기 때문에 사용자가 직접 구분해야한다는 것입니다.


그런데, 이벤트를 사용하게 되면 무조건 Callback임을 알 수 있습니다.

이벤트는 delegate를 등에 엎고 만들어졌습니다.




이벤트는 이렇게 만들어집니다.


그런데, 여기서 event 키워드를 가리면?




대리자가 됩니다. 

하지만, event를 붙이게 되면 그 때서야 이제 event라고 불린다 이겁니다.


또한, 접근 권한이 public입니다.

왜냐, 대리자라면 private이어야합니다. 왜냐, 필드이기 때문입니다.

그런데, 이벤트는 필드가 아닙니다.

왜냐, 외부와의 소통 즉, 인터페이싱을 해야하기 때문입니다.


대리자를 통해 callback을 했을 때에는

'나 너 관심있다!'하며 등록 및 해제를 내부적으로 해줬어야합니다.

하지만, 이벤트는 여기서 하나로 묶어 놨습니다.


다시한번 말씀드리지만,

이벤트는 필드가 아닙니다. 외부로 공개되어있는 이벤트 인터페이스일 뿐입니다.

실제로 내부적으로는 이벤트라는 형식이 하나 만들어지고,

Add(), Remove() 메서드가 생성되지요...

따라서 이벤트를 통해 기존 대리자보다 명확해졌다는 사실을 알 수 있습니다.





위의 예시입니다.

그런데, event 키워드를 넣던 빼던 실행은 됩니다.

즉, 이벤트를 사용하거나 사용하지 않거나 똑같다는 소리입니다.

여기서 차이점이라면 event 키워드를 넣는다면 이벤트라고 부르고

빼면, 대리자라고 부를 뿐입니다.


그런데, Event를 넣음으로써 이벤트가 두개라는 명확한 의미를 가지게 됩니다.

결과를 알고 싶을 때 result를 등록하면 되고,

점수를 알고 싶을 때 score를 등록하면 되기 때문입니다.


그리고 다시 한번 정리하자면, 

이벤트 자체로 인터페이스로 사용되기 때문에 

가시성은 public으로 되어있어야한다는 사실을 기억하시길 바랍니다.


이벤트에서도 역시,

이벤트 발생 시 처리하고자하는 핸들러 메서드를 쉽게 등록할 수 있도록

'+=', '-=' 연산자를 제공해줍니다.

해당 연산자들을 우리는 '이벤트 등록'이라고 부릅니다.


여기서 혹시나하여 말씀드리자면,

아무리 이벤트에 관심이 있다고 등록을 했어도 이벤트가 발생하지 않을 수 있습니다.

이벤트가 발생했을 경우에만 호출이 되기 때문이지요.


아무튼...

그렇다면 이벤트와 대리자의 차이점은 무엇일까요?

우선 이벤트를 쓰는 첫번째 목적은 '명확성'입니다.

대리자는 callback이 아닌 부분에서도 사용을 하지요.



callback은 클라이언트와 서버가 명확하게 나뉩니다.

이벤트가 발생하는 곳은 Server가 하고,

Client는 등록, 제거 및 핸들러를 만들어 둡니다.

이벤트에서 관심이 있는 놈을 등록, 제거를 클라이언트에서 한다는 것입니다.

또한 여기서 핸들러는 처리입니다. 이벤트를 발생했을 때의 처리 말입니다.


정리하자면,

이벤트를 보고 받고 처리하는 것은 Client가 담당합니다.

어떻게 처리할 지에 대한 프로세스가 Client에 있다 이겁니다.

이게 바로 Callback의 메커니즘입니다.


아무튼... 이벤트를 사용하는 첫번째 이유는 바로 'Callback의 명확화'입니다.


두번째로 이벤트는 대리자가 아니기 때문에 

대리자에서사용하던 메서드들을 사용할 수 없습니다.



또한, '=' 즉, 대입 연산이 안됩니다. 참조자이기 때문입니다.


오직 등록과 제거만 가능합니다.

등록, 제거만 하기 때문에 오류날 일이 없겠지요... 아마도..

아무튼... 대리자에서는 메서드들을 등록하고 공개해둔다면,

많은 핸들러들을 등록했어도 null 하나 넣으면 끝이었습니다.

하지만, 이벤트에서는 등록과 제거의 기능만을 제공해주니 이런 문제가 발생할리가 없지요.


이벤트는 필드가 아니기 때문에

오직, '+=', '-=' 연산을 통해 등록 및 해제만 가능하다는 사실을 기억하시길 바랍니다.


세번째는 이벤트는 필드가 아니기 때문에 '인터페이스'에 놓일 수 있다는 사실입니다.

인터페이슨에는 메서드 시그니처만 올 수 있습니다. 

필드는 오지 못합니다.

그런데 사실 메서드 시그니처, 상수, 이벤트만이 올 수 있지요.


여기서 중요한 것은 이벤트도 인터페이스 내에 사용될 수 있다 이겁니다.

callback이라는 것을 구현해야한다는 명시적인 의미만을 가지고 있기 때문입니다.


인터페이스를 구현하는 사람들에게

반드시 이 이벤트, 메서드를 구현하라는 알림을 인터페이스를 통해 하는 것입니다.


정리해볼까요?

우선 이벤트는 

첫번째로 명확해집니다. 

즉, callback 메커니즘을 사용한다는 것이 대리자보다 훨씬 더 명확해 진다 이겁니다.


두번째로 필드가 아니다 보니 여러가지 메서드를 갖고 접근 권한을 갖고 이런 것이 없습니다.

추가적으로 외부에 공개되는 인터페이스로 사용되기 때문에 public으로 되어있지요.

또한 오직 등록 및 제거만을 만들어놨습니다.

즉, 오직 핸들러만을 등록하도록 만들었다 이겁니다.


마지막으로 이벤트가 인터페이스에 올 수 있다는 특징이 있습니다.

대리자는 필드이기 때문에 올 수 없지요.


하... 글이 길어지군요...

이벤트에서 이벤트 핸들러는 정형화 없이 막 만들어지지 않습니다.

즉, 규격화 해뒀다는 소리입니다.

(뭐... 물론 우리가 만들고 싶은데로 만들어도 되긴 하지만...)


이벤트는 delegate 기반으로 만들어지다 보니,

이벤트에는 delegate가 필요합니다.


C#에서 제공해주는 이벤트 핸들러는 이와 같습니다.



제네릭 형식으로 제공해주는 Eventhandler는 

매개변수로 누구로 부터 온 것인지에 대한 sender와 

이벤트 발생시 필요한 부가 정보에 대한 EventArgs 클래스를 상속받은 

이벤트 형식(T)이 인자로 오게됩니다.


즉, 항상 넘기고자하는 이벤트를 

EventArgs라는 클래스를 상속받은 형식을 사용하자고 약속했다는 것입니다.

서로간의 약속이지요.


자세한 내용은 아래에서 예시를 통해 설명하도록 하겠습니다.


자...

CarEnginehandler 라는 핸들러를 등록한다고 해봅시다.

이 때 약속해뒀습니다.



첫번째 인자로는 sender 즉, 이벤트를 발생시키는 객체(this)

두번째 인자로는 EventArgs 즉, 정보를 넘깁니다.


이벤트 처리에 필요한 추가 정보를 담아서 보내고 싶을 때에

EventArgs 클래스를 상속 받아서 클래스를 정의하면 됩니다.


그런데 맨날 



이렇게 만들면... 불편할 수 있기에

닷넷에서 제공하는 BCL에는 이벤트 핸들러를 Generic으로 만들어 놨습니다.

즉, 대리자를 맨날 만들어써야하다보니,

이벤트를 사용할 대리자를 BCL에서 제네릭으로 만들어 놨다는 것입니다.

우리는 T형식만 결정하면 되게 만들었다 이겁니다.


직접 위의 코드와 같이 작성해도 되지만,



이처럼 간단하게 제공해준다 이겁니다.



이렇게 사용하던 것을 간단하게



이처럼 사용할 수 있게되었다 이겁니다.


delegate를 등에 엎어서 사용했었는데, BCL에서 간단하게 사용하도록 제공을 해줬습니다.

generic은 클래스의 이름이 될 것이고,

해당 코드가 실행되는 순간 delegate 하나가 만들어집니다.


항상 이벤트를 만들 때에는 아래와 같이 만들게됩니다.

참고하시길 바랍니다.




정리해보자면,

서버는 이벤트를 정의하고, 발생시켜주는 역할을 하게 되고,

클라이언트는 이벤트를 등록하고 핸들러를 정의해야합니다.

즉, 이벤트를 받을 준비를 하는 것입니다.


아직 람다에 대해서는 배우지 않았지만, 

우선 이벤트 핸들러에 대한 내용을 좀 살펴보고자합니다.




이벤트를 등록한 뒤에

이벤트 발생시 자세한 정보를 포함시키고 싶다면 어떻게 해야할까요?


EventArgs는 이벤트가 발생했을 때 같이 전달할 인수를 만들기 위한 부모 클래스입니다.

따라서, EventArgs를 상속받은 클래스에 전달하고자하는 내용을 구현해주면 됩니다.


물론, 직접 구현해서 이벤트 핸들러를 등록할 경우에도

이벤트 발생시 정보를 넘기고 싶다면 똑같이 해주면 됩니다.



이번 시간은 여기까지 하도록 하겠습니다.


이상 삽잡이였습니다!