[C#] Delegate, Func, Action 이해하기: 차이점 및 사용 사례
C#의 Delegate, Func, Action은 C/C++의 함수 포인터와 비슷한 개념으로, 이벤트 처리 및 비동기 프로세스 처리 등을 위해 유연하고 재사용 가능한 코드를 작성하는 데 필수적인 요소다.
본 포스팅에서는 Delegate, Func, Action 각각의 개념, 차이점, 사용 사례에 대해 설명한다.
1. Delegate
Delegate는 특정 메서드 시그니처(입력 매개변수와 반환 타입)와 일치하는 메서드를 참조할 수 있는 타입이다. 이를 통해 함수를 변수처럼 다룰 수 있다.
델리게이트는 명시적으로 정의되어야 하며, 호출하려는 메서드와 일치하는 시그니처를 가져야 한다. 예를 들어, 두 개의 int
매개변수를 사용하고 float
를 반환하는 메서드를 참조하는 델리게이트를 정의하려면 다음과 같이 사용할 수 있다.
public delegate float CustomDelegate(int a, int b);
public class MyClass
{
public static float Divide(int a, int b)
{
return (float)a / b;
}
}
public class MainClass
{
public static void Main()
{
CustomDelegate del = MyClass.Divide;
float result = del(10, 5);
Console.WriteLine("Result: " + result);
}
}
이 경우, CustomDelegate
델리게이트는 두 개의 int
입력 매개변수를 받아들이고 float
값을 반환하는 MyClass.Divide
메서드를 참조한다.
델리게이트는 메서드 참조를 저장하는 데 사용되며, 호출 시 해당 메서드를 실행한다. 델리게이트는 하나의 메서드뿐만 아니라 여러 개의 메서드를 가리킬 수 있다(멀티캐스팅 델리게이트).
public delegate void MyDelegate(string message);
public class Example
{
public static void PrintMessage1(string message)
{
Console.WriteLine("Message 1: " + message);
}
public static void PrintMessage2(string message)
{
Console.WriteLine("Message 2: " + message);
}
public static void Main()
{
MyDelegate del = PrintMessage1;
del += PrintMessage2; // 델리게이트에 두 번째 메서드 추가
del("Hello, Multicast!"); // 두 메서드를 순차적으로 호출
}
}
위 코드를 실행하면 출력은 다음과 같다.
Message 1: Hello, Multicast!
Message 2: Hello, Multicast!
2. Func
Func는 반환 타입이 void가 아닌 0~16개의 매개변수를 가진 함수를 나타내는 제네릭 델리게이트다. 제네릭(generic)이란 프로그래밍 언어에서 타입에 종속되지 않고, 재사용 가능한 코드를 작성하는 방법이다.
public class MyClass
{
public static int GetNumber()
{
return 42;
}
public static string ToString(int number)
{
return "Number: " + number;
}
}
public class MainClass
{
public static void Main()
{
Func<int> numberFunc = MyClass.GetNumber;
int number = numberFunc();
Console.WriteLine("Number: " + number);
Func<int, string> toStringFunc = MyClass.ToString;
string result = toStringFunc(42);
Console.WriteLine(result);
}
}
이 경우, Func<int>
는 매개변수가 없고 int
값을 반환하는 MyClass.GetNumber
메서드를 참조한다. 그리고 Func<int, string>
은 하나의 int
매개변수를 받고 string
을 반환하는 MyClass.ToString
메서드를 참조한다.
Func도 멀티캐스팅이 가능하지만, 반환 값을 가지기 때문에 마지막으로 추가된 메서드의 반환 값이 최종적으로 반환됨에 유의해야 한다.
using System;
public class Example
{
public static int Add(int x, int y)
{
Console.WriteLine("Add: " + (x + y));
return x + y;
}
public static int Multiply(int x, int y)
{
Console.WriteLine("Multiply: " + (x * y));
return x * y;
}
public static void Main()
{
Func<int, int, int> func = Add;
func += Multiply; // Func에 두 번째 메서드 추가
int result = func(3, 4); // 두 메서드를 순차적으로 호출, 마지막 메서드의 반환 값이 최종 반환됨
Console.WriteLine("Result: " + result);
}
}
위 코드의 실행 결과는 다음과 같다.
Add: 7
Multiply: 12
Result: 12
3. Action
Action은 반환 타입이 void인 메소드를 위해 특별히 설계된 제네릭 델리게이트다. Action은 Func와 마찬가지로 사용자 정의 델리게이트 대신 사용할 수 있어 코드를 간소화하고 표현력을 높일 수 있다. 매개변수도 Func와 마찬가지로 0~16개를 가질 수 있다. Action도 멀티캐스팅이 가능하다.
public class MyClass
{
public static void PrintHello()
{
Console.WriteLine("Hello!");
}
public static void PrintSum(float a, float b)
{
Console.WriteLine("Sum: " + (a + b));
}
}
public class MainClass
{
public static void Main()
{
Action helloAction = MyClass.PrintHello;
helloAction();
Action<float, float> sumAction = MyClass.PrintSum;
sumAction(3.5f, 5.5f);
}
}
이 경우, Action
은 매개변수가 없는 MyClass.PrintHello
메서드를 참조한다. 그리고 Action<float, float>
는 두 개의 float
매개변수를 받는 MyClass.PrintSum
메서드를 참조한다.
이벤트 핸들러 등의 경우 특정 시그니처가 요구되는데(특정한 형식의 매개변수를 받고, 특정한 형식의 값을 반환해야 하는데), 이 경우 해당 시그니처를 명확히 표현하는 Delegate를 사용하는 것이 더 명확하고 직관적이다. 예를 들어, 클릭 이벤트 핸들러는 (object sender, EventArgs e) 형식을 받아야 한다. (Action으로 대체가 가능은 하다.)
// Delegate 사용
public delegate void ClickEventHandler(object sender, EventArgs e);
class Program
{
public static event ClickEventHandler ClickEvent;
static void Main()
{
ClickEvent += OnClick;
ClickEvent?.Invoke(null, EventArgs.Empty);
}
static void OnClick(object sender, EventArgs e)
{
Console.WriteLine("Clicked");
}
}
// Action 사용
class Program
{
public static event Action<object, EventArgs> ClickEvent;
static void Main()
{
ClickEvent += OnClick;
ClickEvent?.Invoke(null, EventArgs.Empty);
}
static void OnClick(object sender, EventArgs e)
{
Console.WriteLine("Clicked");
}
}
Summary
- Delegate: 특정 메서드 시그니처(입력 매개변수와 반환 타입)와 일치하는 메서드를 참조할 수 있는 타입이다.
- Func: 반환 값이 있는 메서드를 참조하는 제네릭 델리게이트다. 마지막 타입 매개 변수는 반환 타입을 나타내며, 나머지 매개 변수는 입력 매개 변수의 타입이다.
- Action: 반환 값이 없는(void) 메서드를 참조하는 제네릭 델리게이트다. 모든 타입 매개 변수는 입력 매개 변수의 타입을 나타낸다.
- 모두 멀티캐스팅이 가능하나, 반환값이 있는 Delegate/Func를 여러개 캐스팅한 경우 그 Delegate/Func의 반환값은 멀티캐스팅 시 마지막으로 추가된 메서드의 반환값이 된다.
C# Delegate, Func, Action은 코드의 유연성과 재사용성을 높이는 데 도움이 되는 도구이다. 이들의 개념과 차이점을 이해하고 적절한 상황에 사용하면 효율적인 코드를 작성할 수 있다.
이 글을 통해 각각의 개념과 사용 사례에 대해 더 잘 이해할 수 있기를 바란다.