Search
Duplicate

뇌를 자극하는 C# 4.0 프로그래밍/ 델리게이트와 이벤트

델리게이트란?

// 델리게이트 선언 방법 한정자 delegate 반환형식 델리게이트이름 (매개변수목록); // 델리게이트 사용 예시 int Plus(int a, int b) { return a + b; } int Minus(int a, int b) { return a - b; } delegate int MyDelegate(int a, int b); MyDelegate Callback; Callback = new MyDelegate(Plus); Console.WriteLine(Callback(3, 4)); Callback = new MyDelegate(Minus); Console.WriteLine(Callback(7, 5));
C#
C#에서 델리게이트는 콜백(Callback)을 구현하기 위해 사용된다.
델리게이트는 메서드에 대한 참조이다.
델리게이트에 메서드의 주소를 할당한 후 델리게이트를 호출하면 이 델리게이트가 메서드를 호출해 준다.
델리게이트는 여러 메서드를 참조할 수 있기 때문에 –델리게이트 체인– 옵저버 패턴을 쉽게 구성할 수 있게 해준다.

델리게이트는 왜, 그리고 언제 사용하나요?

값이 아닌 코드 자체를 매개변수로 넘기고 싶을 때 델리게이트를 사용한다.
여러 곳에서 하나의 동일한 코드를 실행하는데, 코드 완료 후 각기 다른 메서드를 실행해야 하는 경우, 코드를 받는 입장에서 코드 로직은 같지만 자신의 코드를 완료한 후 실행해야 하는 메서드가 달라져 여러 메서드를 따로 만들어야 하는 중복 코드가 발생하게 된다.
이 때 코드 완료 후에 실행해야 하는 각기 다른 메서드 자체를 매개변수로 넘겨주면 코드를 받는 입장에서는 자신의 코드를 완료한 후에 매개변수로 넘겨 받은 메서드를 실행해 버리면 되기 때문에 코드가 매우 간편해 진다.
이런 역할을 하는게 바로 델리게이트이다. 매개변수로 메서드를 넘길 수 있게 해줘서 코드 중복을 줄이는 것.
더불어 델리게이트는 델리게이트 체인을 이용하여 델리게이트에 등록된 여러 메서드들을 한번에 실행하게 하는 강력한 기능을 갖고 있는데, 이는 이벤트라는 기능으로 이어진다.
(매개변수를 넘기는 예시 생략)

일반화 델리게이트

// 일반화 델리게이트 사용 예시 delegate int Compare<T> (T a, T b); static int AscendCompare<T> (T a, T b) { return a.CompareTo(b); }
C#
일반화 메서드와 동일하게 델리게이트도 일반화 할 수 있다.

델리게이트 체인

// 델리게이트 체인 사용 예시 void Call119 (string location) { Console.WriteLine("소방서죠? 불났어요! 주소는 {0}", location); } void ShotOut (string location) { Console.WriteLine("피하세요! {0}에 불이 났어요!", location); } void Escape (string location) { Console.WriteLine("{0}에서 나갑시다!", location); } delegate void ThereIsAFire(string location); // +를 이용해서 하나의 델리게이트에 여러 메서드를 넣을 수 있다. ThereIsAFire fire = new ThereIsAFire(Call119); fire += new ThereIsAFire(ShotOut); fire += new ThereIsAFire(Escape);
C#
델리게이트는 하나가 여러 개의 메서드를 동시에 참조할 수 있다는 특징이 있다.
하나의 델리게이트에 여러 메서드를 넣으려면 + 기호를 이용하면 된다.
만일 델리게이트에 넣어진 메서드 중 하나를 제거해야 한다면  기호를 이용하면 된다.

익명 메서드

// 익명 메서드의 선언 방법 델리게이트 인스턴스 = delegate(매개변수목록) { // 실행 코드 } // 익명 메서드 사용 예시 public static void Main() { Calculate Calc; Calc = delegate(int a, int b) { return a + b; } Console.WriteLine("3 + 4: {0}", Calc(3, 4)); }
C#
델리게이트가 참조할 메서드를 넘겨야 할 일이 생겼는데 이 메서드를 두 번 사용할 일이 없다고 판단되면 위와 같이 메서드를 익명으로 만들어 사용하면 된다.

이벤트: 객체에 일어난 사건 알리기

// 이벤트가 발생했을 경우 그것을 구독자에게 알려줄 발행자 클래스. // 발행자는 구독자가 누구인지 모르고 다만 구독자들에게 이벤트가 발생했을 시 그것을 알리는 역할만 한다. class Publisher { //이벤트를 위한 델리게이트 정의. 델리게이트 자체는 밖에 있어도 무방하다. public delegate void MyEventHandler(); // 델리게이트에 event 한정자를 붙여서 이벤트를 정의한다. // 델리게이트 체인 기능을 이용하여 이벤트가 발생했을시 구독하는 객체들의 메서드를 모두 실행한다. public event MyEventHandler Click; // 이벤트 발생시 실행되는 메서드 public void DoClick() { if(Click != null) // 이 객체의 이벤트를 구독하는 구독자가 있는지 검사 { // 이벤트 발생. 구독자들에게 이벤트를 통지함.(델리게이트 호출) Click(); } } } // 이벤트를 구독하는 구독자(Subscriber) 클래스 // 이벤트가 발생했을시 그것을 들을지 말지는 각 클래스에서 스스로 정한다. class Subscriber { static void Main() { // 이벤트 발행자 객체 생성 Publisher p = new Publisher(); // 이벤트를 구독한다. // 이벤트가 발생했을시 실행할 자신의 메서드를 등록해 둔다. p.Click += new Publisher.MyEventHandler(p_Click); } // Publisher 객체에서 Click 이벤트가 발생할 때 호출되는 메서드 // 델리게이트 호출을 통해 이 메서드를 이벤트에 등록해 둔다. // 이벤트 구독자는 여럿일 것이고, 발행자는 이벤트가 발생했을시 델리게이트 체인을 이용하여 각 구독자가 등록해 둔 메서드들을 모두 실행하게 된다. static void p_Click() { Console.WriteLine("Publisher 객체의 Click 이벤트가 발생하였습니다"); } }
C#
(책에 나온 설명보다 좀 더 잘 정리된 내용으로 정리. 이벤트는 publisher-subscriber 관계로 이해하는게 가장 명확하다. 발행자-구독자 관계로 보면 C#의 이벤트는 옵저버 패턴을 담고 있다고 할 수 있다.)
C#에서 이벤트는 델리게이트를 event 한정자로 수식해서 만든다. 이벤트가 델리게이트 형식인 이유는 이벤트가 발생했을 시 델리게이트 체인을 이용하여 이벤트 구독자들에게 한 번에 알리기 위함. 이벤트를 갖고 있는 클래스가 발행자가 된다.
이벤트 구독자들은 이벤트가 발생했을 시 실행할 메서드를 이벤트에 등록해 두어서 이벤트가 실행되면 메서드가 자동으로 실행되게 한다.
이벤트는 외부에서 실행시킬 수 없으며, 오로지 발행자만이 이벤트를 실행할 수 있다. 이벤트 구독자들은 이벤트가 발생했을시 실행할 메서드를 등록할 수만 있다. 이는 보안 관련 이슈인데 자세한 내용은 다음 섹션에서 설명.

델리게이트와 이벤트

이벤트와 델리게이트의 가장 큰 차이점은 이벤트는 외부에서 직접 사용할 수 없다는 것. 반면 델리게이트는 외부에서 얼마든지 호출할 수 있다.
이벤트가 외부에서 호출될 수 없다는 사실은 견고한 이벤트 기반 프로그래밍을 가능하게 한다. 이벤트는 발행자만 발생시킬 수 있기 때문에 –누구나 들을 수는 있지만– 신뢰할 수 있다.