MFC 개발자분들이 매일 사용하시던 '함수 포인터(Function Pointer)' 개념이 C#에서 가장 현대적이고 안전하게 진화한 형태라고 보시면 됩니다.
이 문법이 의미하는 바를 각 키워드와 MFC 개념을 매핑하여 명쾌하게 풀어드리겠습니다.
## 1. MFC 함수 포인터 vs C# Func 대리자
- MFC 방식 (typedef): 특정 함수를 변수처럼 넘기려면 리턴 타입과 파라미터를 명시한 typedef를 매번 선언해야 했습니다. 32비트/64비트 환경이나 호출 규약(__stdcall, __cdecl)에 따라 컴파일 에러가 나기도 쉬웠죠.
// MFC 스타일: 매번 타입을 정의해야 함
typedef int (*MY_CALC_FUNC)(double);
- .NET 8 방식 (Func): 프레임워크가 자주 쓰는 함수 포인터 형태를 제네릭(Generic, 구조체의 템플릿과 유사)으로 미리 다 만들어 두었습니다. 개발자는 typedef 정의 없이 그냥 Func<double, int>라고 적기만 하면 됩니다.
## 2. 정의문 한 줄씩 해부하기
public delegate TResult Func<in T, out TResult>(T arg);
이 짧은 문장에 담긴 현대 C#의 핵심 메커니즘은 다음과 같습니다.
### ① delegate (대리자)
C++의 typedef 함수 포인터 선언과 같습니다. "이것은 일반 데이터 변수가 아니라, 함수의 메모리 주소를 담을 수 있는 대리자 변수 타입이다"라고 선언하는 것입니다.
### ② Func<in T, out TResult> (제네릭 파라미터)
- <...>: C++의 템플릿(template <typename T>)과 같습니다. 어떤 데이터 타입이든 유연하게 받아들이겠다는 뜻입니다.
- in T: 함수가 입력받을 매개변수(Argument)의 타입입니다. in 키워드는 반공변성(Contravariance)을 의미하며, "입력값은 더 넓은 데이터 타입(부모 클래스)도 안전하게 호환된다"는 것을 컴파일러에게 알려줍니다.
- out TResult: 함수의 최종 리턴(반환) 타입입니다. out 키워드는 공변성(Covariance)을 의미하며, "결과값은 더 구체적인 타입(자식 클래스)으로 안전하게 내보낼 수 있다"는 뜻입니다.
### ③ (T arg)
이 함수 포인터가 가리킬 실제 함수의 매개변수 구조입니다. 위에서 정의한 T 타입의 변수 arg를 하나 입력받겠다는 뜻입니다.
## 3. 실무 코드 예시 (데이터 처리)
이 Func 대리자가 실제 전력 제어 소프트웨어에서 어떻게 강력하게 쓰이는지 구체적인 예시를 보여드리겠습니다.
장치에서 올라온 Raw 값을 받아서, 특정 규칙이나 배율을 적용해 변환 결과를 리턴하는 가상의 변환 엔진 구조입니다.
public class CmrDataProcessor
{
// Func<double, double> 변수는:
// "double을 입력받아서 double을 리턴하는 모든 함수"를 담을 수 있는 함수 포인터입니다.
public double ApplyCalibration(double rawCurrent, Func<double, double> calibrationAlgorithm)
{
// 넘겨받은 함수 포인터(대리자)를 실행하여 결과를 리턴
return calibrationAlgorithm(rawCurrent);
}
}
이제 이 함수를 호출할 때, 과거 MFC처럼 복잡하게 함수 주소(&)를 넘길 필요 없이 현대 C#의 람다식(Lambda Expression)을 이용해 익명 함수를 한 줄 만에 즉석에서 주입할 수 있습니다.
var processor = new DataProcessor();
double rawData = 1000.0; // 장치에서 읽은 1000 [cite: 33, 39]
// 예시 1: 오차율 1.02배를 곱하는 알고리즘 포인터를 즉석 주입
double result1 = processor.ApplyCalibration(rawData, x => x * 1.02);
// 예시 2: 특정 임계치(800)를 넘으면 값을 보정하는 복잡한 알고리즘 포인터 주입
double result2 = processor.ApplyCalibration(rawData, x => {
if (x > 800) return x - 50;
return x;
});
## 💡 MFC 개발자를 위한 최종 요약
public delegate TResult Func<in T, out TResult>(T arg);는
**"어떤 타입(T)이든 인자로 딱 하나 입력받아서, 원하는 타입(TResult)으로 결과를 돌려주는 '표준화된 함수 포인터 변수 뼈대'"**를 의미합니다.
앞서 학습하신 ResiliencePipeline 내부에서 ExecuteAsync를 호출할 때나, IServiceCollection에서 런타임에 동적으로 객체를 생성하는 팩토리 패턴을 짤 때 이 Func 대리자가 람다식과 결합하여 핵심적인 역할을 수행하게 됩니다.