Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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
Archives
Today
Total
관리 메뉴

nomad-programmer

[Programming/C#] 대리자 (Delegate) a.k.a (함수 포인터) 본문

Programming/C#

[Programming/C#] 대리자 (Delegate) a.k.a (함수 포인터)

scii 2020. 9. 22. 02:31

대리자와 이벤트

사건을 영어로는 이벤트(Event)라고 하는데, 컴퓨터에 발생하는 이벤트에 반응하도록 프로그램을 만드는 것을 일컬어 "이벤트 기반 프로그래밍(Event Driven Programming)" 이라고 부른다. 멀티 패러다임 언어인 C#이 지원하는 또 하나의 프로그래밍 패러다임인 셈이다.
이벤트 기반 프로그래밍은 GUI(Graphic User Interface) 를 만들 때 특히 유용하다. C#에서 지원하는 이벤트 기반 프로그래밍을 이해하려면 먼저 대리자를 알아야 하고 그 다음 이벤트를 알아야 한다.

2020/06/16 - [Programming/C] - [C] 함수 포인터

2020/06/16 - [Programming/C] - [C] 콜백 함수

2020/06/18 - [Programming/C] - [C] 구조체와 열거형 그리고 함수 포인터

대리자란?

콜백(Callback)이란, 비서처럼 대신 어떤 일을 해줄 코드를 두고, 이 코드가 실행할 세부 코드는 컴파일 시점이 아닌 실행 시점에 부여한다.
C#에서 대리자는 바로 이 콜백을 구현하기 위해 사용된다. 대리자(delegate)에는 영어로 "대리인" 또는 "사절" 이라는 뜻이 있다. 즉, 누군가를 대신해서 일해주는 것을 전문으로 하는 사람을 의미한다. 
대리자는 "메소드에 대한 참조 (함수 포인터)" 이다. 대리자에 메소드의 주소를 할당한 후 대리자를 호출하면 이 대리자가 메소드를 호출해준다.

한정자 delegate 반환형식 대리자이름 (매개변수 목록);

대리자는 메소드에 대한 참조이기 때문에 자신이 참조할 메소드의 반환 형식과 매개 변수를 명시해줘야 한다. 그리고 대리자는 인스턴스가 아닌 형식(Type)이다. 다시 말해 MyDelegate는 int, string 과 같은 형식이며 메소드를 참조하는 그 무엇을 만들려면 MyDelegate의 인스턴스를 따로 만들어야 한다는 말이다. C언어에서의 함수 포인터를 생각하면 이건 당연한 이야기다.

대리자를 이용하여 콜백을 구현하는 과정

  1. 대리자를 선언한다. (함수 포인터를 선언한다)
  2. 대리자의 인스턴스를 생성한다. 생성할 때 대리자가 참조할 메소드를 매개 변수로 넘긴다. (함수 포인터 변수에 호출할 함수의 주소값을 저장한다)
  3. 대리자를 호출한다. (함수 포인터를 실행한다 ex) *(funcPtr)(1, 3);)
using System;

namespace test
{
    delegate int MyDelegate(int a, int b);

    class Calulator
    {
        public int Plus(int a, int b)
        {
            return a + b;
        }

        public static int Minus(int a, int b)
        {
            return a - b;
        }
    }

    internal class Program
    {
        public static void Main(string[] args)
        {
            Calulator calc = new Calulator();
            MyDelegate callback;

            callback = new MyDelegate(calc.Plus);
            Console.WriteLine(callback(3, 7));

            callback = new MyDelegate(Calulator.Minus);
            Console.WriteLine(callback(7, 2));
        }
    }
}


/* 결과

10
5

*/

프로그래밍을 하다 보면 "값" 아닌 "코드" 자체를 매개 변수로 넘기고 싶을 때가 있다. 예를 들어 배열을 정렬하는 메소드를 만든다고 가정해보자. 메소드가 오름차순으로 혹은 내림차순으로 아니면 특별한 계산식을 거쳐 나오는 결과 순으로 정렬하도록 하려면 값이 아닌 코드를 넘겨야 편안한 프로그래밍이 가능해진다.
메소드가 정렬을 수행할 때 사용하는 비교 루틴을 매개 변수로 넣을 수 있으면 메소드를 사용하는 프로그래머가 알아서 하면 된다. 그리고 이런 때 바로 대리자가 사용된다. 
대리자는 메소드에 대한 참조이므로, 비교 메소드를 참조할 대리자를 매개 변수로 받도록 정렬 메소드를 작성해 놓으면 된다.

대리자를 이용한 버블 정렬 예제

using System;

namespace test
{
    delegate int Compare(int a, int b);

    internal class Program
    {
        static int AscendCompare(int a, int b)
        {
            if (a > b)
                return 1;
            else if (a == b)
                return 0;
            else
                return -1;
        }

        static int DescendCompare(int a, int b)
        {
            if (a < b)
                return 1;
            else if (a == b)
                return 0;
            else
                return -1;
        }

        static void BubbleSort(int[] dataSet, Compare comparer)
        {
            int temp = 0;

            for (int i = 0; i < dataSet.Length - 1; i++)
            {
                for (int j = 0; j < dataSet.Length - (i + 1); j++)
                {
                    if (comparer(dataSet[j], dataSet[j + 1]) > 0)
                    {
                        temp = dataSet[j + 1];
                        dataSet[j + 1] = dataSet[j];
                        dataSet[j] = temp;
                    }
                }
            }
        }

        public static void Main(string[] args)
        {
            int[] array = {3, 7, 4, 2, 10};
            
            Console.WriteLine("Sorting ascending...");
            BubbleSort(array, new Compare(AscendCompare));

            for (int i = 0; i < array.Length; i++)
                Console.Write($"{array[i]} ");
                
            int[] array2 = {7, 2, 8, 10, 11};
            Console.WriteLine("\nSorting descending...");
            BubbleSort(array2, new Compare(DescendCompare));

            for (int i = 0; i < array2.Length; i++)
                Console.Write($"{array2[i]} ");
            
            Console.WriteLine();
        }
    }
}


/* 결과

Sorting ascending...
2 3 4 7 10 
Sorting descending...
11 10 8 7 2 

*/
Comments