[ C# ] Delegate

2024. 3. 11. 18:16C#

delegate : 대리자


특정 매개 변수 목록 및 반환 형식이 있는 함수에 대한 참조를 나타내는 형식.

 

'전화 주세요' 메모처럼 어떤 일을 수행하는 코드를 콜백(callback)이라고 부르는데,

콜백(callback)을 맡아 실행하는 일을 대리자(delegate)가 담당한다.

 

쉽게 이야기하면 delegate(대리자)함수에 대한 참조이다.

 

선언 방식

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

 

public delegate int MyDelegate(int a, int b);

 

이렇게 보면 함수와 비슷해보이지만, 대리자는 인스턴스가 아니라 형식(Type)이다.

즉, MyDelegate는 int, double, string과 같은 형식이며 "함수를 참조하는 어떤 것"을 만들려면

MyDelegate의 인스턴스를 따로 만들어야 한다는 것이다.

 

그럼 참조할 함수들을 만들어보자.

이때, 함수는 반환 타입과 매개변수가 참조하려는 delegate와 동일해야한다.

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

 

함수들까지 모두 준비되었다.

이제 MyDelegate에 해당 함수들을 참조시켜 보자

MyDelegate Callback;

Callback = new MyDelegate(Plus);
Console.WriteLine(Callback(3, 4));

Callback = new MyDelegate(Minus);
Console.WriteLine(Callback(5, 2));

 

해당 코드들을 실행시키면 아래와 같은 결과가 출력된다.

7
3

 

 

요약

1. 대리자를 선언한다.

2. 선언한 대리자가 참조할 함수를 선언한다.

    (이때, 반환형과 매개변수가 일치해야 한다.)

3. 대리자의 인스턴스를 만들고 대리자가 참조할 함수를 매개변수로 넘긴다.

4. 대리자를 호출한다.


코드를 보면 그냥 함수를 호출하면 될 것을 어째서 대리자를 이용하는지 의문이 든다.

 

그러나 다르게 생각해 보자.

코드를 작성하다 보면 값뿐만 아니라 코드 자체를 매개 변수에 집어넣고 싶을 때가 있다.

 

예를 들면 sort 키워드를 사용하지 않고 배열을 정렬시킬 때처럼 말이다.

 

대리자는 함수에 대한 참조이므로, 

비교 함수를 참조할 대리자를 매개 변수에 받을 수 있도록 정렬 함수를 작성하면 손쉽게 해결된다.

 

1. 대리자 선언

public delegate int Compare(int a, int b);

 

2. 대리자가 참조할 함수 선언

static int AsCompare(int a, int b)	// 두 수의 비교
{
	if(a>b)	return 1;
    else if(a==b)	return0;
    else	return -1;
}

 

3. 정렬 함수 작성

static void MySort(int[] array, Compare compare)
{
	for(int i =0; i< array.Length-1; i++)
    {
    	for(int k=0; k< array.Length-1; k++)
        {
        	if(compare(array[i],array[k]) > 0)
            {
            	int temp = array[k+1];
                array[k+1] = array[k];
                array[k] = temp;
            }
        }
    }
}

 

4. 정렬 함수를 호출한다.

 

int[] array = {3, 10, 7, 1, 5};
MySort(array, new Compare(AsCompare));

 

일반화 대리자


대리자는 일반화 함수도 참조할 수 있다.

 

위의 배열 정렬 코드를 리팩토링

delegate int Compare<T>(T a, T b);

static int AsCompare<T>(T a, T b)	where T : IComparable<T>
{
	return a.CompareTo(b);
}

static void MySort<T>(T[] array, Compare<T> compare)
{
	for(int i =0; i< array.Length-1; i++)
    {
    	for(int k=0; k< array.Length-1; k++)
        {
        	if(compare(array[i],array[k]) > 0)
            {
            	int temp = array[k+1];
                array[k+1] = array[k];
                array[k] = temp;
            }
        }
    }
}

 

AsCompare 함수가 이전과는 많이 다른 것을 볼 수 있다.

이는 매개 변수이자 일반화 변수인 a와 b는 비교 연산자로 비교할 수 없어서 발생한 것으로

CompareTo() 함수로 이를 대체하였다.


대리자 체인


delegate 하나가 여러 개의 함수들을 동시에 참조할 수 있는 속성을 이야기한다.

(엄밀히 따지면 동시에 처리되는 것이 아닌 체인의 순서대로 처리된다.)

 

대리자들은 += 연산자를 이용하여 새로운 함수를 결합하거나 -= 연산자를 이용해 특정 대리자를 끊어낼 수 있다.

 public delegate void TestDel(int a, int b);
 
 public static void SumNumber(int a, int b)
 {
     Console.WriteLine($"덧셈 : {a+b}");
 }
 public static void MulNumber(int a, int b)
 {
     Console.WriteLine($"곱셈 : {a*b}");
 }
 
 static void Main(string[] args)
 {
 	TestDel testDel = SumNumber;
	testDel += MulNumber;
	testDel.Invoke(10, 20);
    
    testDel -= MulNumber;
    testDel.Invoke(10, 20);
 }

 

( Invoke : 델리게이트에 등록된 메서드들을 순차적으로 실행시킨다.)

 

+= 연산자로 인하여 대리자인 testDel에 SumNumber와 MulNumber가 체인 되어 있는 것을 알 수 있다.

이를 실행하면 아래와 같은 결과가 나타난다.

덧셈 : 30
곱셈 : 200

 

그 이후 -= 연산자로 MulNumber 함수를 제거하였고 이는 결과로 바로 확인할 수 있다.

덧셈 : 30

익명 메서드


이름이 없는 함수를 의미한다.

 

함수는 분명 이름이 존재해야 선언하거나 사용할 수 있는 거 아닌가?

 

대리자를 사용하면 이를 극복할 수 있다.

 

선언 방식

대리자_인스턴스 = delegate (매개변수 목록) { /* 실행하려는 코드 */ };

 

대리자_인스턴스를 호출하면 delegate의 코드를 실행한다.

'C#' 카테고리의 다른 글

반복기 Iterator  (0) 2024.03.13
[ C# ] Event (정의와 delegate와의 차이점)  (0) 2024.03.11
[ C# ] Getter와 Setter 그리고 Property  (0) 2024.03.05
[ C# ] Class 의 상속  (0) 2024.03.05
[C#] 값 형식과 참조 형식  (0) 2024.03.04