1. 델리게이트(Delegate)


오늘은 델리게이트(Delegate)에 대해서 알아보려고 합니다. 델리게이트는 한마디로 말해서 대리자라고 말할 수 있습니다. 즉, 대신 일을 해주는 녀석이라고 할 수 있겠죠? 다른 말로 해서는 메소드 참조를 포함하고 있는 영역이라고 말할 수 있습니다. 아래는 델리게이트의 선언 형식입니다.

delegate 반환형 델리게이트명(매개변수..);

위의 선언 형식을 보아하니, 델리게이트가 메소드같이 생겼죠? 델리게이트는 위에서 말한 대로, 메소드의 참조를 포함합니다. '메소드를 참조한다니, 그럼 메소드를 매개변수로 넘길 수 있다는 말인가요?'라고 생각하시는 분들이 있을지 모르겠는데, 네 맞습니다. 델리게이트를 이용해서 메소드를 넘겨줄 수 있습니다. 델리게이트는 메소드를 참조 하는 것이고, 참조하는 메소드가 달라진다면 델리게이트 역시 달라집니다. (한가지 주의하실 것이 있다면, 매개변수의 데이터 형식과 반환형은 참조할 메소드의 매개변수의 데이터 형식과 반환형에 맞추어야만 합니다. 개수 역시도.)


무엇보다 이 델리게이트란 녀석이 어떤 일을 하는지는, 직접 해보는게 더 빠를지도 모릅니다. 아래는 델리게이트의 사용 예제입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication39
{
    delegate int PDelegate(int a, int b);

    class Program
    {
        static int Plus(int a, int b)
        {
            return a + b;
        }

        static void Main(string[] args)
        {
            PDelegate pd1 = Plus;
            PDelegate pd2 = delegate(int a, int b)
            {
                return a / b;
            };

            Console.WriteLine(pd1(5, 10));
            Console.WriteLine(pd2(10, 5));
        }
    }
}

결과:

15

2

계속하려면 아무 키나 누르십시오 . . .


코드를 보시면 9행에 PDelegate라는 델리게이트가 보이죠? 매개변수 부분에는 int형 매개변수 a, b를 명시해주었습니다. 13~16행에서는 Plus란 메소드가 정의되었습니다. 이제부터 자세히 보셔야 합니다. 20행을 먼저 봅시다. Plus 메소드 자체를 델리게이트에 집어넣고 있는것 같죠? Plus 메소드와 연결하여 대리자를 인스턴스화 합니다. 이제부터 델리게이트 pd1은 Plus 메소드를 참조하게 됩니다. 26행을 보시면 Plus 메소드를 쓰듯, a와 b를 더해서 값을 반환합니다. 21행을 보시면 아무 이름이 없는 메소드를 델리게이트에 집어넣습니다. 별도로 메소드를 만들지 않았죠? 이런 무명 메소드의 사용은 한번 사용하면 불필요해지는 메소드를 만들때도 사용되는 등, 매우 유용합니다. 27행도 26행과 마찬가지로 pd1처럼 사용할 수 있습니다.


2. 델리게이트 체인(Delegate chain)


델리게이트 체인이라고 하니, 무언가가 체인처럼 이어져 있을거라고 생각이 들지 않나요? 네, 맞습니다. 델리게이트 하나를 가지고 여러개의 메소드를 한번에 호출할 수 있습니다. 한번 볼까요?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication39
{
    delegate void PDelegate(int a, int b);

    class Program
    {
        static void Plus(int a, int b)
        {
            Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
        }

        static void Minus(int a, int b)
        {
            Console.WriteLine("{0} - {1} = {2}", a, b, a - b);
        }

        static void Division(int a, int b)
        {
            Console.WriteLine("{0} / {1} = {2}", a, b, a / b);
        }

        static void Multiplication(int a, int b)
        {
            Console.WriteLine("{0} * {1} = {2}", a, b, a * b);
        }

        static void Main(string[] args)
        {
            PDelegate pd = (PDelegate)Delegate.Combine(new PDelegate(Plus),
                new PDelegate(Minus), new PDelegate(Division), new PDelegate(Multiplication));

            pd(20, 10);
        }
    }
}

결과:

20 + 10 = 30

20 - 10 = 10

20 / 10 = 2

20 * 10 = 200

계속하려면 아무 키나 누르십시오 . . .


코드를 보시면 13~31행에 사칙연산(더하기, 빼기, 나누기, 곱셈) 메소드가 정의된 것을 보실 수 있습니다. 이제, 델리게이트를 이용하여 이 모두를 묶어주어 한꺼번에 호출할 수 있습니다. 35~36행을 보세요. Delegate.Combine 메소드가 쓰였습니다. 이 메소드는 두개 이상의 대리자의 호출 목록을 연결해주는 역할을 합니다. Plus와 Minus, Division, Multiplication 메소드는 이제 델리게이트 pd만 호출하여도 간편하게 모두를 호출할 수 있습니다.


3. 이벤트(Event)


이벤트는 실제론 '잔치', '행사'와 같은 의미로 쓰이나 프로그래밍 세계에 와서는 의미가 '특정 사건이 벌어지면 알리는 메시지'라고 할 수 있습니다. 이벤트를 예로 들자면, 사용자가 컨트롤(버튼, 이미지, 레이블, 텍스트박스 등..)을 클릭하거나 창을 닫거나 열때 사용자에게 개체를 알리는 것을 이벤트 발생이라고 합니다. C#에서는 우리가 방금 배운 델리게이트를 사용하여 이벤트를 처리할 수 있습니다. 이벤트의 기본적인 선언 형식은 다음과 같습니다.

한정자 event 델리게이트 이름;

위와 같이 event 키워드를 사용하여 이벤트를 선언할 수 있습니다. 한번 직접 보도록 해볼까요?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication42
{
    public delegate void MyEventHandler(string message);

    class Publisher
    {
        public event MyEventHandler Active;

        public void DoActive(int number)
        {
            if (number % 10 == 0)
                Active("Active!" + number);
            else
                Console.WriteLine(number);
        }
    }

    class Subscriber
    {
        static public void MyHandler(string message)
        {
            Console.WriteLine(message);
        }

        static void Main(string[] args)
        {
            Publisher publisher = new Publisher();
            publisher.Active += new MyEventHandler(MyHandler);

            for (int i = 1; i < 50; i++)
                publisher.DoActive(i);
        }
    }
}

결과:

1

..

Active!10

..

Active!40

..

49

계속하려면 아무 키나 누르십시오 . . .


자 이제, 코드를 볼까요? 9행을 보시면 MyEventHandler라는 델리게이트가 선언되었습니다. 11행을 보시면 Publisher라는 클래스가 선언되었고, 이 클래스 내에는 event 한정자로 수식한 델리게이트의 인스턴스를 선언합니다. 그리고 DoActive라는 메소드도 정의되었습니다. number라는 매개변수를 가지고, number를 10으로 나눴을때 나머지가 0이면, 이벤트가 발생하게끔 했습니다. 그게 아니라면 그냥 숫자만을 출력하도록 했습니다. 이제 Subscriber 클래스로 내려와서 바로 보이는게 이벤트 핸들러 부분인데, 위에서 선언한 델리게이트와 일치합니다. 그리고 더 아래로 내려가면 클래스의 인스턴스를 만들고 Active 이벤트에 MyHandler 메소드를 이벤트 핸들러로 등록하라는 말과 같습니다. 이제 아래서 특정 조건을 만족하면 이벤트가 발생하며 Active가 출력됩니다. 이벤트가 조금 생소하죠?

궁금하신 부분은 언제나 물어보시고, 보충해야 할 부분이 있다면 추가 설명을 달도록 하겠습니다.


이번 강좌는 설명이 많이 부족하고, 애매한 부분이 많았던것 같습니다. 오늘은 여기까지 하겠습니다.


수고하셨습니다. 다음 강좌는 리플렉션과 애트리뷰트(Reflection and attributes)에 대해 알아보려고 합니다.