C# 제네릭 대리자(Generic Delegate)란?
대리자 형식은 제네릭(generic) 형식을 매개 변수로 이용할 수 있다. 제네릭을 지원하면, 대리자를 변수로 초기화할 때까지 대리자에 사용할 매개 변수나 반환 형식을 지정을 미룰 수 있다. 즉, 대리자 형식을 정의할 때는 대리자의 매개 변수와 반환 형식을 명시하지 않는다. 다음 코드를 이용해서 좀 더 자세히 알아보자.
public partial class Program
{
private delegate T FormulaDelegate<T>(T a, T b);
}
using System;
public partial class Program
{
private delegate T FormulaDelegate<T>(T a, T b);
private static int Addint(int a, int b)
{
return a + b;
}
private static double Adddouble(double a, double b)
{
return a + b;
}
static void Main(string[] args)
{
FormulaDelegate<int> IntAddDel = Addint;
FormulaDelegate<double> DoubleAddDel = Adddouble;
Console.WriteLine("Invoking intAddtion(3, 4)");
Console.WriteLine($"Result = {IntAddDel(3, 4)}");
Console.WriteLine("Invoking doubleAddtion(2.2, 3.3)");
Console.WriteLine($"Result = {DoubleAddDel(2.2, 3.3)}");
}
}
int와 double 둘 다 처리가 가능한 제네릭 대리자를 생성하였다.
예제를 통해 하나의 대리자 형식을 이용해서 시그니처가 다른 두 개의 메서드를 참조하는 대리자들을 선언할 수 있다는 것을 알았다. intAddDel 대리자는 매개 변수와 반환 형식이 int인 Addint() 메서드를 참조하며, DoubleAddDel 대리자가 참조하는 메서드의 매개 변수와 반환 형식은 double이다. 대리자가 참조하는 메서드의 데이터 형식이 무엇인지는 대리자를 초기화할 때 꺽쇠(<>)안에 표기한 데이터 형식을 이용해서 정의한다. 다음은 제네릭 데이터 형식을 이용하는 대리자의 초기화 코드의 예다.
FormulaDelegate<int> IntAddDel = Addint;
FormulaDelegate<double> DoubleAddDel = Adddouble;
이처럼 데이터 형식을 정의함으로써 대리자가 참조 대상 메서드의 데이터 형식과 일치 여부를 확인할 수 있다. 시그니처가 다른 함수들을 호출할 수 있는 것도 이런 구조 덕분이다.
지금까지 하나의 제네릭 매개 변수를 가지는 대리자를 살펴봤다. 다음은 대리자 선언 시, 제네릭 매개 변수를 여러 개 사용할 수 있음을 보여주는 예시다.
public partial class Program
{
private delegate void AdditionDelegate<T1, T2>(T1 value1, T2 value2);
}
AdditionDelegate 대리자는 형식이 다른 두 개의 매개 변수 T1과 T2를 가지며, 이들은 변수 선언 시에 정의할 데이터 형식을 의미한다. 자세한 예를 들기 위해 서로 다른 형식의 매개 변수를 두 개씩 가지는 AddIntDouble()과 AddFloatDouble() 메서드를 살펴보자.
using System;
public partial class Program
{
private delegate void AdditionDelegate<T1, T2>(T1 value1, T2 value2);
private static void AddIntDouble(int a, double b)
{
Console.WriteLine($"{a} + {b} = {a + b}");
}
private static void AddFloatDouble(float a, double b)
{
Console.WriteLine($"{a} + {b} = {a + b}");
}
static void Main(string[] args)
{
AdditionDelegate<int, double> AddIntDoubleDel = AddIntDouble;
AdditionDelegate<float, double> AddFloatDoubleDel = AddFloatDouble;
Console.WriteLine("Invoking AddIntDouble(3, 4.3)");
AddIntDoubleDel(3, 4.3);
Console.WriteLine("Invoking AddFloatDouble(1.2f, 2.3)");
AddFloatDoubleDel(1.2f, 2.3);
}
}
이번에는 반환값을 갖는 다중 템플릿 대리자를 만들어보자. 대리자 선언은 다음과 같다.
using System;
public partial class Program
{
private delegate TResult AddAndConvert<T1, T2, TResult>(T1 digit1, T2 digit2);
private static float AddIntDouble(int a, double b)
{
Console.WriteLine($"(int){a} + (double){b} = (float){(float)(a + b)}");
return (float)(a + b);
}
private static int AddFloatDouble(float a, double b)
{
Console.WriteLine($"(float){a} + (double){b} = (int){(int)(a + b)}");
return (int)(a + b);
}
static void Main(string[] args)
{
AddAndConvert<int, double, float> AddIntDoubleDel = AddIntDouble;
AddAndConvert<float, double, int> AddFloatDoubleDel = AddFloatDouble;
Console.WriteLine("Invoking intDoubleAddConvertToFloat delegate(5, 3.9)");
Console.WriteLine(AddIntDoubleDel(5, 3.9));
Console.WriteLine("Invoking floatDoubleAddConvertToInt delegate(4.3, 2.1)");
Console.WriteLine(AddFloatDoubleDel((float)4.3, 2.1));
}
}
다중 템플릿 제네릭 형식의 대리자를 이용하면 상이한 시그니처를 갖는 다양한 메서드에 대응할 수 있다.
Action<>과 Func<>
C#은 최대 16개의 매개 변수를 갖는 void 반환 형식의 내장 대리자인 Action을 제공한다. 즉, Action 대리자는 매개 변수를 0~16개 가지면서 값을 반환하지 않는 메서드를 호출할 수 있다. 덕분에 대리자를 선언할 필요성이 대폭 줄어들었으며 손쉬운 메서드 참조가 가능해졌다.
다음은 MultiTemplateDelegates의 VoidDelegateInvoke()에 Action 대리자를 적용한 ActionDelegateInvoke() 메서드다.
public partial class Program
{
private static void AddIntDouble(int a, double b)
{
Console.WriteLine($"(int){a} + (double){b} = (float){(float)(a + b)}");
}
private static void AddFloatDouble(float a, double b)
{
Console.WriteLine($"(float){a} + (double){b} = (int){(int)(a + b)}");
}
private static void ActionDelegateInvoke()
{
Action<int, double> intDoubleAddAction = AddIntDouble;
Action<float, double> floatDoubleAddAction = AddFloatDouble;
Console.WriteLine("Invoking intDoubleAddConvertToFloat delegate(5, 3.9)");
intDoubleAddAction(1, 2.5);
Console.WriteLine("Invoking floatDoubleAddConvertToInt delegate((float)1.2, 4.3)");
floatDoubleAddAction((float)1.2, 4.3);
}
}
미리 정의된 Action<> 델리게이트로 교체하였다. 별도로 Delegate를 선언하지 않아도 되서 유용하게 사용이 가능하다.
다음은 TResult를 반환하는 경우를 Func<> 델리게이트로 변경하였다.
동일하게 맨 위에 선언되어 있던 Delegate 구문은 이제 더 이상 선언하지 않아도 된다.
public partial class Program
{
private static float AddIntDouble(int a, double b)
{
Console.WriteLine($"(int){a} + (double){b} = (float){(float)(a + b)}");
return (float)(a + b);
}
private static int AddFloatDouble(float a, double b)
{
Console.WriteLine($"(float){a} + (double){b} = (int){(int)(a + b)}");
return (int)(a + b);
}
static void Main(string[] args)
{
Func<int, double, float> AddIntDoubleFunc = AddIntDouble;
Func<float, double, int> AddFloatDoubleFunc = AddFloatDouble;
Console.WriteLine("Invoking intDoubleAddConvertToFloat delegate(5, 3.9)");
Console.WriteLine(AddIntDoubleFunc(5, 3.9));
Console.WriteLine("Invoking floatDoubleAddConvertToInt delegate(4.3, 2.1)");
Console.WriteLine(AddFloatDoubleFunc((float)4.3, 2.1));
}
}
함께 읽으면 좋은 글
출처
'프로그래밍 > C#' 카테고리의 다른 글
식 트리와 람다식 (0) | 2024.03.27 |
---|---|
C# 공변성(Covariance)이란 무엇인가? (0) | 2024.03.26 |
C# list 랜덤(random) 하게 섞기 (0) | 2024.03.21 |
C# 비트 연산자를 이용한 홀수짝수 판별, 절반으로 나누기 함수 구현 (0) | 2024.03.18 |
C# char를 int로 바꾸는데 '0'을 빼는 이유 (0) | 2024.02.26 |
댓글