익명 델리게이트를 이해하려면, “델리게이트 = 메서드를 가리키는 변수 타입”이라는 개념을 먼저 짚고 가야 한다.
delegate bool NumberTest(int n);
// int 하나 받아서 bool(true/false)을 반환하는 메서드를 가리킬 수 있는 타입
이제 이 델리게이트 타입을 이용해 이런 식으로 쓸 수 있다.
static bool IsEven(int n) { return n % 2 == 0; }
static bool IsOdd(int n) { return n % 2 != 0; }
static int Count(int[] numbers, NumberTest test)
{
int count = 0;
foreach (int n in numbers)
{
if (test(n)) // test가 가리키는 메서드를 호출
count++;
}
return count;
}
int[] arr = { 3, 5, 4, 2, 6, 7, 8 };
int evenCount = Count(arr, IsEven);
int oddCount = Count(arr, IsOdd);
여기까지는 “이름 있는 메서드(IsEven, IsOdd)를 델리게이트에 넘겨서 쓰는 패턴”이다.
익명 델리게이트(anonymous delegate)는 말 그대로 “이름 없는 메서드”를 즉석에서 만들어서 델리게이트 자리에 넣는 문법이다.
방금 예제를 익명 델리게이트로 바꾸면 이렇게 쓸 수 있다.
int[] arr = { 3, 5, 4, 2, 6, 7, 8 };
// 짝수 개수
int evenCount = Count(
arr,
delegate(int n)
{
return n % 2 == 0;
}
);
// 홀수 개수
int oddCount = Count(
arr,
delegate(int n)
{
return n % 2 != 0;
}
);
여기서 포인트는 이 부분이다:
delegate(int n)
{
return n % 2 == 0;
}
delegate(int n) { ... } → 이름이 없는 메서드(익명 메서드)NumberTest 타입 델리게이트로 들어감IsEven, IsOdd 같은 별도 메서드를 안 만들고, 그 자리에서 조건을 바로 적어버리는 것즉, “이름 없는 메서드를 즉석에서 만들어서 델리게이트로 넘기는 것”이 익명 델리게이트다.
주요 이유는 크게 세 가지 정도로 정리할 수 있다.
예를 들어, 아래처럼 쓸 수도 있다.
// 이름 있는 함수 버전
static bool GreaterThanTen(int n)
{
return n > 10;
}
int count = Count(arr, GreaterThanTen);
// 익명 델리게이트 버전
int count2 = Count(
arr,
delegate(int n)
{
return n > 10;
}
);
동일한 동작을 하지만, “딱 여기에서 한 번만 쓸 조건”이라면 익명 델리게이트를 쓰는 편이 코드가 더 모여 있고 보기 좋을 수 있다.
아래는 간단한 콘솔 프로그램 전체 코드다.
using System;
delegate bool NumberTest(int n); // 조건을 표현하는 델리게이트 타입
class Program
{
static void Main()
{
int[] arr = { 3, 5, 4, 2, 6, 7, 8, 11, 13, 15, 20 };
// 1) 짝수 개수 - 익명 델리게이트 사용
int evenCount = Count(
arr,
delegate(int n)
{
return n % 2 == 0;
}
);
// 2) 홀수 개수
int oddCount = Count(
arr,
delegate(int n)
{
return n % 2 != 0;
}
);
// 3) 10보다 큰 수 개수
int greaterThanTenCount = Count(
arr,
delegate(int n)
{
return n > 10;
}
);
Console.WriteLine("짝수 개수: " + evenCount);
Console.WriteLine("홀수 개수: " + oddCount);
Console.WriteLine("10보다 큰 수 개수: " + greaterThanTenCount);
Console.ReadKey();
}
// 공통 Count 함수: 조건을 델리게이트로 받음
static int Count(int[] numbers, NumberTest test)
{
int count = 0;
foreach (int n in numbers)
{
if (test(n)) // 여기서 test가 가리키는 익명 메서드가 실제로 호출됨
count++;
}
return count;
}
}
이 코드를 실행해보면,
IsEven, IsOdd 같은 별도 메서드 정의 없이도
조건을 그 자리에서 바로 적어서 Count 할 수 있다는 점이 보인다.
익명 델리게이트는 특히 이벤트 핸들러에서 많이 쓰였다 (C# 2.0 시대 코드들에서 자주 볼 수 있는 패턴).
button1.Click += Button1_Click;
void Button1_Click(object sender, EventArgs e)
{
MessageBox.Show("버튼 클릭!");
}
button1.Click += delegate(object sender, EventArgs e)
{
MessageBox.Show("버튼 클릭!");
};
차이점은,
Button1_Click이라는 메서드를 따로 만들어서 연결실제 동작은 완전히 동일하다. 다만 한 번만 쓸 간단한 핸들러라면, 익명 델리게이트를 쓰는 쪽이 코드를 더 모아두는 효과가 있다.
요즘 C# 코드를 보면, 익명 델리게이트보다는 람다식(lambda)을 훨씬 많이 쓴다. 사실 람다식은 익명 델리게이트의 “짧은 문법 버전”이라고 보면 된다.
int evenCount = Count(
arr,
delegate(int n)
{
return n % 2 == 0;
}
);
int evenCount = Count(
arr,
n => n % 2 == 0
);
delegate(int n) { return n % 2 == 0; }
→ 매개변수 타입+본문을 풀로 쓴 형태n => n % 2 == 0
→ 같은 의미를 가진 더 짧은 표현(람다식)결국 내부에서는 둘 다 NumberTest 델리게이트로 취급된다. 문법만 간단해졌을 뿐, “함수를 값처럼 넘긴다”라는 근본 개념은 똑같다.