[C#] Delegate - 콜백, 체인
in C# on 공부, C#, Delegate, Callback, Chain
Delegate(델리게이트)
Delegate의 사전적 의미는 ‘대리인’이다. c++의 포인터와 비슷하게 생각하면 된다.
이 대리인이라는 것은 내가 할 일을 다른 사람에게 위임하는 것이다. 프로그래밍에서도 이런 대리인 역할을 해주는게 존재하는데, 그게 Delegate이다.
델리게이트는 메소드를 대신해서 호출하는 역할을 한다. 즉 메소드의 대리인 역할을 하는 것이다. 원래는 특정 메소드를 처리할 때 그 메소드를 직접 호출해서 실행시켜야 했지만, 델리게이트를 사용하면 그 메소드를 대신해서 호출할 수 있다.
Delegate 선언, 참조
Delegate로 특정 메소드를 대신 호출하려면
- 해당 메소드와 동일한 타입의 델리게이트 타입을 선언
- 선언한 델리게이트 타입으로 델리게이트 변수를 생성하고, 생성한 델리게이트 변수에 해당 메소드를 참조시킨다.(포인터와 유사하다)
먼저 동일한 타입의 델리게이트 타입을 선언한다.
// delegate 리턴타입 이름(매개 변수);
delegate void typeA(void); // void funcA(void)
delegate void typeB(int); // void funcB(int)
delegate float typeC(float); // float funcC(float)
타입으로 델리게이트 변수를 생성해서 그 변수에 메소드를 참조시킨다.
// 변수 생성
typeA delegate_a;
typeB delegate_b;
typeC delegate_c;
// 메소드 참조
delegate_a = new typeA(funcA);
delegate_b = new typeB(funcB);
delegate_c = new typeC(funcC);
이 방법을 적용하여 덧셈과 뺄셈을 하는 코드를 작성해보자.
namespace Sample {
delegate int DelegateType(int a, int b);
class MainApp {
public static int Plus(int a, int b){ return a + b; }
public static int Sub(int a, int b){ return a - b; }
static void Main(string[] args){
DelegateType delegateVar;
delegateVar = new DelegateType(Plus);
int sum = delegateVar(11, 12);
Console.WriteLine("11 + 12 - {0}", sum);
delegateVar = new DelegateType(Sub);
int sub = delegateVar(11, 12);
Console.WriteLine("11 - 12 - {0}", sub);
}
}
}
Callback method
위의 더하기 빼기 코드를 보면 한 델리게이트 변수에 한 함수를 참조하게 하고 다음에 바로 참조 위치를 바꿔버린다. 그런데 이렇게 델리게이트를 쓰는 것은 정말 의미가 없다. 메소드를 직접 호출하면 되고, 굳이 델리게이트를 쓸 이유가 없어지는 것이다.
델리게이트는 콜백 메소드를 구현할 때 진가를 발휘한다.
콜백(Callback)이란 A라는 메서드를 호출할 때 B라는 메서드를 넘겨줘서 A가 B 메서드를 호출하도록 하는 것이고, 이때 A를 콜백 메서드라고 한다.
구조를 보면 A 메서드 안에 Delegate를 선언하고, 이 Delegate로 다른 함수를 호출할 수 있는데 이게 B 메서드를 참조하는 것이다.
예제는 아래와 같다.
namespace Sample {
delegate int DelegateType(int a, int b);
class MainApp {
public static void Calculator(int a, int b, DelegateType delegateVar){
Console.WriteLine(delegateVar(a,b));
}
public static int Plus(int a, int b){ return a + b; }
public static int Sub(int a, int b){ return a - b; }
static void Main(string[] args){
DelegateType plus = new DelegateType(Plus);
DelegateType sub = new DelegateType(Sub);
Calculator(11, 12, plus);
Calculator(11, 12, sub);
}
}
}
정말 간편하다. 계산기 함수를 호출할때마다 원하는 연산(메소드)를 넘겨줘서 계산기가 기능을 하도록 하고 있다.
Delegate 일반화
위에서는 Delegate의 타입을 선언할 때 참조할 수 있는 메서드 타입을 지정했지만, 이를 일반화하면 타입과 상관 없이 메소드를 참조할 수 있다.
namespace Sample {
delegate T DelegateType<T>(T a, T b);
class MainApp {
public static void Calculator<T>(T a, T b, DelegateType<T> delegateVar){
Console.WriteLine(delegateVar(a,b));
}
public static int PlusInt(int a, int b){ return a + b; }
public static float PlusFloat(float a, float b){ return a + b; }
static void Main(string[] args){
DelegateType plusInt = new DelegateType(PlusInt);
DelegateType plusFloat = new DelegateType(PlusFloat);
Calculator(11, 12, plusInt);
Calculator(1.1f, 2.2f, plusFloat);
}
}
}
Delegate Chain
위에서는 하나의 델리게이트가 하나의 메소드를 참조할 수 있게 했다. 그런데 델리게이트는 여러개의 메소드도 참조할 수 있다.
아주 간편한게, 아래와 같이 +, += 연산자로 새로운 메소드를 추가해주면 된다.
DelegateType delegateVar;
delegateVar = new DelegateType(func1);
delegateVar += func1;
delegateVar += func2;
이제 델리게이트를 호출하면 참조된 메소드들을 차례대로 호출한다. 이렇게 하나의 델리게이트에 여러 메소드를 연결시키는 것을 델리게이트 체인이라고 한다.
반대로 델리게이트에 연결된 메소드를 끊고 싶으면 -=를 해주면 된다.
namespace Sample {
delegate void delType();
class MainApp {
public static void func1() { Console.Write("first"); }
public static void func2() { Console.Write("second"); }
public static void func3() { Console.Write("third"); }
static void Main(string[] args){
delType delVar;
delVar = new delType(func1);
delVar += func2;
delVar += func3;
delVar();
delVar -= func1;
delVar -= func2;
delVar();
}
}
}