CodeGym /행동 /C# SELF /익명 메서드 소개 ( delegate)

익명 메서드 소개 ( delegate)

C# SELF
레벨 49 , 레슨 0
사용 가능

1. 소개

코딩하다 보면 한 번만 쓰이고 더 이상 필요 없는 간단한 동작을 기술해야 할 때가 있어. 그런 걸 위해 굳이 클래스에 따로 메서드를 만드는 건 비행기 조립 키트에서 못질하는 것처럼 과한 짓일 때가 있지. 그럴 땐 근처에서 바로 해결하는 게 훨씬 편해.

익명 메서드는 이름이 필요 없는 일시적인 코드 조각으로, 선언된 자리에서 바로 쓰이는 경우가 많아. 특징은:

  • 다른 메서드 내부에서 선언된다.
  • 이름이 없다.
  • 보통 한 번만 쓰인다 — 예를 들어 delegate에 넘기거나 이벤트에 구독할 때.

요즘은 람다 표현식(=>)을 훨씬 많이 쓰지만, 익명 메서드를 이해하면 delegate와 이벤트 모델의 동작 원리를 더 잘 알 수 있어.

언제 익명 메서드가 편할까?

  • 정렬, 필터링, 이벤트 같은 곳에 간단한 로직을 빠르게 넘기고 싶을 때.
  • 일회성 로직 때문에 클래스에 메서드를 잔뜩 만들고 싶지 않을 때.
  • 코드를 간결하게 유지하고 싶을 때: 작은 비즈니스 로직을 한 곳에 몰아두면 읽기 좋다.

약간의 역사

C#에서 익명 메서드 기능은 처음에 2.0에서 도입됐어. 그 전엔 delegate를 쓰려면 이름 있는 메서드를 따로 만들어야 해서 번거로웠지. 익명 메서드로 훨씬 간단해졌고, 람다가 나오면서 그 간결함은 더 강화됐어.

2. 기본: delegate와 전통적인 이름 있는 메서드

익명 메서드를 보기 전에 delegate를 통해 동작을 전달하는 전통적인 방식을 복습해보자. 예를 들어 '도서 DB' 앱에서 책을 다양한 조건으로 필터링한다고 하자.

// 델리게이트 정의
public delegate bool BookFilter(Book book);

// 책 클래스
public class Book
{
    public string Title { get; set; }
    public int Year { get; set; }
}

예전에는 이렇게 썼어:

// 별도의 필터 함수
public static bool IsClassic(Book b)
{
    return b.Year < 1970;
}

// 어딘가에서 사용
BookFilter filter = IsClassic;

매번 별도 메서드를 만드는 건 편하지 않아. 필터가 수십 개면 더 난감하고.

3. 익명 메서드: 미니멀하고 delegate와 찰떡

익명 메서드를 쓰면 필터 코드를 필요한 자리에서 바로 정의할 수 있어:

BookFilter filter = delegate(Book b)
{
    return b.Year < 1970;
};

끝이야! 불필요한 메서드 없이 그 자리에서 바로 정의했지. delegate가 이름 없는 함수 선언 역할을 해.

일반 문법

delegate([매개변수])
{
    // 메서드 본문
};

프로그램에 끼워넣은 예:

public class Book
{
    public string Title { get; set; }
    public int Year { get; set; }
}

public delegate bool BookFilter(Book book);

class Program
{
    static void Main()
    {
        Book[] books = {
            new Book { Title = "마스터와 마르가리타", Year = 1967 },
            new Book { Title = "Clean Code", Year = 2008 }
        };

        // 클래식용 익명 필터
        BookFilter filter = delegate(Book b)
        {
            return b.Year < 1970;
        };

        foreach (Book book in books)
        {
            if (filter(book)) // 익명 함수 호출!
                Console.WriteLine($"{book.Title} — 고전!");
        }
    }
}

출력:

마스터와 마르가리타 — 고전!

4. 다양한 delegate와 함께하는 익명 메서드

익명 메서드는 모든 delegate에서 잘 동작해. 예를 들어 표준인 ActionFunc<T, TResult>도 물론 사용 가능해. 이건 나중에 더 자세히 볼 거야.

Action<string> sayHello = delegate(string name)
{
    Console.WriteLine($"안녕, {name}!");
};

sayHello("세계"); // 안녕, 세계!

간단한 비유: 한 번 쓰는 임시 직원

익명 메서드를 잠깐 왔다가 금방 일 끝내는 임시 직원처럼 생각하면 쉬워. delegate로 직원에게 일을 맡기는 거지.

Func<int, int, int> sum = delegate (int a, int b) {
    return a + b;
};
Console.WriteLine(sum(5, 7)); // 12

적용 예: 정렬, 검색, 컬렉션 처리

예를 들어 책 리스트를 발행 연도 기준으로 정렬하고 싶다면, 일회성 비교 함수를 따로 만들 필요가 없어.

var books = new List<Book>
{
    new Book { Title = "마스터와 마르가리타", Year = 1967 },
    new Book { Title = "Clean Code", Year = 2008 }
};

books.Sort(delegate(Book a, Book b)
{
    return a.Year.CompareTo(b.Year);
});

foreach (var book in books)
    Console.WriteLine($"{book.Title} ({book.Year})");

5. 유용한 팁

익명 메서드와 람다 표현식은 다른가?

최신 C#에서는 람다 표현식(=>)을 더 자주 쓰고, 보통 차이가 크지 않지만 몇 가지 중요한 차이가 있어:

  • 람다는 더 짧고 표현력이 좋다.
  • 람다의 변수 캡처 규칙이 명확하다(나중에 배우자).
  • 익명 메서드는 먼저 도입된 문법이라 오래된 코드에서 가끔 보인다.

같은 예제를 람다로 쓰면 이렇게 된다:

BookFilter filter = b => b.Year < 1970;

옛 문법과 새 문법 둘 다 아는 게 인터뷰나 다른 사람 코드 읽을 때, delegate 내부 동작을 깊게 이해할 때 도움이 된다.

매개변수가 있는/없는 익명 메서드

delegate가 아무 매개변수를 받지 않으면 한 줄로도 쓸 수 있어:

Action printHello = delegate { Console.WriteLine("안녕!"); };
printHello(); // 안녕!

매개변수가 있어도 한 줄로 쓸 수 있어:

Action<int> printSquare = delegate (int x) { Console.WriteLine(x * x); };
printSquare(6); // 36

익명 메서드 vs 람다 표현식

익명 메서드 람다 표현식
문법
delegate (...) {}
(args) => {}
변수 캡처 가능 가능
인기 드물게 사용 표준
값 반환 가능/필요할 수 있음 가능/필요할 수 있음
여러 줄 가능 가능

6. 익명 메서드의 세부사항

로컬 변수 캡처
익명 메서드는 감싸고 있는 메서드의 변수를 사용할 수 있는데, 이걸 '클로저'라고 해. 예:

int minYear = 1970;

BookFilter filter = delegate(Book book)
{
    return book.Year < minYear;
};

Console.WriteLine(filter(new Book { Title = "테스트", Year = 1960 })); // True

나중에 minYear를 바꾸면 필터는 새로운 값을 사용하게 된다!

매개변수를 생략할 수 있음
매개변수가 필요 없으면 이렇게:

Action sayHi = delegate { Console.WriteLine("안녕!"); };

null 전달
delegate에 메서드가 할당되지 않으면 값은 null이고, 호출하려 하면 NullReferenceException이 발생해. 조심하자.

여러 줄 코드
익명 메서드는 조건문, 반복문, 다른 호출까지 포함하는 완전한 블록이 될 수 있어:

Action manyThings = delegate
{
    Console.WriteLine("시작!");
    for (int i = 0; i < 3; i++)
        Console.WriteLine(i);
    Console.WriteLine("끝났어!");
};
manyThings();

7. 흔한 실수와 특징

가끔 개발자들은 변수의 스코프 때문에 헷갈려. 루프에서 값을 캡처할 때 결과가 기대와 다를 수 있어.

List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
    actions.Add(delegate { Console.WriteLine(i); });
}
foreach (var action in actions) action(); // 3 3 3 — 놀람!

익명 메서드는 변수 i를 '직접 본다' — 루프가 끝나면 i3이 된다. 그래서 모든 메서드가 같은 값을 출력하지. 올바르게 하려면 현재 값을 캡처해줘야 해:

for (int i = 0; i < 3; i++)
{
    int current = i;
    actions.Add(delegate { Console.WriteLine(current); });
}
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION