Delegate와 Event의 이해(2)

Posted by 나에요임마
2017. 12. 10. 11:21 Program/C#

저번 글에 이어 "

Event

" 에 대해서 알아도보록 하자.

 

2.)   Event(이벤트)란 무엇일까~?

 

    - C#에 있어서 Event란 개념을 단순하게 정의한다면 '자신의 타입 혹은 자신의 인스턴스가 다른 객체에 특별한 일이 일어났음을 통지' 한다라

      고  볼수 있다. MSDN에서는 대략 '한 객체에 뭔가 흥미로운 사건이 발생했을때 특정 Client에 알림을 제공하는 방법'이라고 나와있다.

 

        (본인은 영어에 약해 맘?대로 의역하는 버릇이 있으므로 딴지걸지 마시고 직접가서 확인하실분은 주저말고 아래 링크를 클릭하도록 하자 )

        http://msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx)

 

이벤트를 사용하는 이유는 위에서의 'Event'  정의와 같다고 볼 수 있다. 대표적인 예가 C# 의 'Event Handler' 다.

간단하게 다아는 Winform 내에서 'Button'의 Click 이벤트를 등록하는 부분을 보도록 하자.

 

[Source] 

Code Snippet
  1. this.button1.Click += new System.EventHandler(this.button1_Click);

 

 

 

 

Code Snippet
  1. // Summary:
  2.         //     Occurs when the control is clicked.
  3.         [SRCategory("CatAction")]
  4.         [SRDescription("ControlOnClickDescr")]
  5.         public event EventHandler Click;

 

우리가 보통 화면상 Button 의 Click 이벤트를 더블클릭하게 되면

위와같이 해당 Button 클래스가 가지고 있는 Event 객체인 Click 'EventHandler' 에 등록이 되게 된다.

 

해당 'EventHandler' 란 것도 사실 별것 아닌 앞서 설명한 'Delegate'로 이루어져 있다.

 

[Source]

Code Snippet
  1. public delegate void EventHandler(object sender, EventArgs e);

 

 

눈치채신 분들도 있겠지만

위에 언급된 코드에서  'event' 와 'delegate' 의 차이는 차이는

보통의 앞선 설명에서 사용되던 delegate의 'Type Field' 방식에 'event'란 단어가 붙어있다는 점이다.

 

'event'  'Delegate Type Field'에 대한

하나의 'modifier' 개념과 비슷하다.

(실제 event가 내부적으로 'access moidifer' 라는 건 아니다)

 

즉 위의 'Delegate Type Field' 에 대한 'Access Modifier' 와 같은 개념으로 쓰이는 것이지

'Delegate Type Field' 그 자체를 의미하는 건 아니다.

(

 'event'

 'delegate type field' 를 캡슐화 해준다)

 

'event' 하나로 어떠한 변화가 생기는지 아래의 코드로 확인 해 보도록 하자.

 

[Source]

Code Snippet
  1. class EventTestClass
  2.     {
  3.         public event EventHandler Test;
  4.  
  5.         public void Prn(object sender, EventArgs e)
  6.         {
  7.             Console.WriteLine("{0} Property Changed!", sender.GetType().ToString());
  8.         }
  9.  
  10.         public void PropertyChanged(object sender, EventArgs e)
  11.         {
  12.             if (Test != null)
  13.                 Test(sender, e);
  14.         }
  15.     }

 

 

Code Snippet
  1. class Program
  2.     {
  3.         private int num = 0;
  4.         public EventTestClass eTestClass = new EventTestClass();
  5.  
  6.         public int NUM
  7.         {
  8.             get
  9.             {
  10.                 return num;
  11.             }
  12.             set
  13.             {
  14.                 if (value != num)
  15.                     eTestClass.PropertyChanged(this, EventArgs.Empty);
  16.  
  17.                 num = value;
  18.             }
  19.         }
  20.  
  21.         static void Main(string[] args)
  22.         {
  23.             Program pgm = new Program();
  24.  
  25.             pgm.eTestClass.Test += new EventHandler(pgm.eTestClass.Prn);
  26.  
  27.             pgm.NUM = 4;
  28.         
  29.         }
  30.     }

 

먼저 위와같이 하나의 event 객체를 가진  클래스가 내부적으로 어떤 변화를 거치는지 살펴보자.

 

[Reflector]

 

 

[ILDASM]

 

여기서 잠깐 주목해볼 중요한 점은

 public event EventHandler Test

라고 선언했던 부분의 Eventhandler의 Field가 private 한정자로 바뀌었다는 점이다.

 

'Reflector' 로 까여진 소스내에 보다싶이

컴파일러에 의해서

 

(1) 'Private EventHandler Type Field'

(2) 'Event Handler Property'

 

위 두 가지 코드가 새롭게 생성되었다는 점도 주목해봐야할 곳이다.

 

즉 우리가 'event' 라는 명칭을 붙여주게 되면

위의 (1), (2) 과정이 내부 컴파일러에 의해서 생성되고

 

실제로 우리가 'event handler' 에 등록하기 위해서 '+=' 같은 동작을 하게되면

private 접근자인 'delegate type field' 에 접근하는게 아니라

'event handler property' 에 접근하게 되는 것이다.

 

그 내부 에서는 다시금 각각 '+=', '-=' Operator 에 맞게끔

add, remove 동작이 실행된다.

(코드에서도 보이듯이 각 add, remove 내에서는 private 으로 변경된 'EventHander' 에 대한 조작을 가한다.)

 

* ILDASM에서 보이는 add_, remove_ 이하의 명칭은 컴파일러에 의해 생성된

 'Event Handler Property' 의 add, remove에 대한 Method이다.

 

즉, 이 말이 무슨말인지 알아채신 분도 있겠지만

add, remove내에서 해당 delegate에 대한 조작을 한다는 건

다시말해 event 역시 내부적으로는 delegate 와 같은 방식으로 동작을 하게 된 다는 것이다.

(전에 설명된 Combine, Remove 같은 방식으로 Delegate list를 관리)

 

단지 Delegate 를 쓰는것과 달리 위와 같은 'Event' 를 사용하게 되면

기존의 Delegate의 통지기능에 더해

클래스의 외부 코드가 해당 'EventHandler' Field 에 대한 부적절한 사용을 막아줌과 동시에

이로인해 필드의 값이 변경되 구독한 메서드에 대한 List 들 값에 대한 부적절한 변경을 막아줄 수 있다.

(이는 OOP적인 관점에서 'event' 가 내부적으로 Delegate Type Field 에 대한 'modifier' 적인 개념의 내부 처리를 한다고 볼수있다.)

 

여기에 더해서 한가지 더 특징은

위의 'EventHandler Property' 에 의해 제공되는

add_, remove_이하 메서드는 '쓰레드 안정적' 인 기능을 더해준다는 점이다.

 

[MethodImpl(MethodImplOptions.Synchronized)]

public void add_Test(블러블러......){...}

 

컴파일러 내부에 의해서 add_ 이하 메소드는 위와같은 Attribute 를 지니게 되는데

이는 여러 객체가 Event에 동시에 등록하거나 해제하려 할 경우에 순차적인 접근을 보장해 준다.

 

이상 'Event'에 대한 몇가지 내용을 'Delegate'에 이어서 살펴보았다.

 

위에 언급된 add_, remove_ 이하 메서드인

'Event Handler Property' 는 재정의 해서 쓸수 있으며

이는 위에 언급된 쓰레드 안정적인 Attribute가 가져다 줄 또다른 성능 문제점등의

문제를 해결하기 위해서 사용되나 이글에서는 따로 다루진 않는다.

 

(참고 : http://www.switchonthecode.com/tutorials/csharp-snippet-tutorial-custom-event-handlers)