[로봇활용_11주차] C# 대리자(Delegate)

최윤호·2025년 10월 18일
post-thumbnail

심부름꾼을 고용하는 방법

만약, C#에서 '메서드(기능)' 자체를 변수에 담아서 전달할 수 있다면 어떨까요?
마치 '심부름꾼'에게 오늘은 "A 가게에 가서 우유 사 와!"라고 시키고,
내일은 "B 세탁소 가서 옷 찾아와!"라고 시키는 것처럼,
수행할 임무(메서드) 자체를 동적으로 바꿔서 전달하는 개념이 대리자(Delegate)입니다.

1)대리자(Delegate)란?

대리자(Delegate)는 '메서드에 대한 참조(Reference)'를 저장하는 특별한 타입입니다.
C언어와 C++의 함수 포인터와 비슷하지만, 훨씬 안전하고 객체 지향적인 방식이죠.

비유: 심부름꾼과 임무 명세서

  • 대리자 선언: 먼저 "심부름꾼이 수행할 임무는 반드시 '2개의 정수'를 받고,
    '1개의 정수'를 결과물로 가져와야 한다."라는 '임무 명세서'를 정의합니다.
  • 메서드 작성: 이 명세서에 맞는 실제 임무들, '덧셈하기', '뺄셈하기' 메서드를 만듭니다.
  • 대리자 인스턴스 생성: "김 대리! 자네의 임무는 '덧셈하기' 야!" (실제 임무 할당)
  • 대리자 호출: "김 대리, 숫자 10과 5를 가지고 가서 임무 수행해!"라고 시키면,
    김 대리는 자신에게 할당된 '덧셈하기' 임무를 수행하고 결과를 가져옵니다.

핵심은 '임무 명세서(대리자 타입)'와 '실제 임무(메서드)'의 '서명(Signature)'이 완벽하게
일치해야 한다는 점입니다. 서명이란 메서드의 반환 타입과 매개변수 목록을 의미합니다.
이것이 바로 대리자가 '타입에 안전하다(Type-safe)'고 불리는 이유입니다.

2)대리자 사용법 3단계

자, 그럼 직접 심부름꾼을 코드로 고용해 봅시다.
delegate키워드를 사용하여 대리자 타입을 선언합니다.
정수 두 개를 받아 정수 하나를 반환하는 Calculate라는 명세서를 만들어 보겠습니다.

[코드]

// 1단계: 대리자 선언 (임무 명세서 정의)
// 반환 타입: int, 매개변수: (int, int)
// 이 서명과 일치하는 모든 메서드를 참조할 수 있는 'Calculate' 타입을 정의
public delegate int Calculate(int a, int b);

// 2단계: 대리자에 할당할 메서드 작성 (실제 임무 정의)
// Calculate 명세서와 서명이 똑같은 Add와 Subtract 메서드를 만듭니다.
public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Subtract(int a, int b)
    {
        return a - b;
    }
}

// 3단계: 대리자 인스턴스 생성 및 호출 (심부름꾼 고용 및 실행)
// 이제 Calculate 타입의 '심부름꾼'을 고용하고, 실제 임무를 맡긴 뒤 실행해 보겠습니다.
class Program
{
    static void Main()
    {
        Calculator calculator = new Calculator();

        // 1. 'calc'라는 심부름꾼(대리자 인스턴스)에게 'Add' 임무를 할당
        Calculate calc = new Calculate(calculator.Add); // 정식 문법
        // Calculate calc = calculator.Add; // 더 간결한 문법도 가능

        // 2. 대리자 호출: calc를 호출하면 실제로는 Add 메서드가 실행됨
        int result = calc(10, 5);
        Console.WriteLine($"결과: {result}"); // 출력: 결과: 15

        // 3. 이제 'calc' 심부름꾼에게 다른 임무를 맡겨보자!
        calc = calculator.Subtract;

        // 4. 다시 대리자 호출: 이제는 Subtract 메서드가 실행됨
        result = calc(10, 5);
        Console.WriteLine($"결과: {result}"); // 출력: 결과: 5
    }
}

[실행 결과]

결과: 15
결과: 5

동일한 코드가, 대리자에 어떤 메서드가 할당되어 있느냐에 따라
전혀 다른 결과를 만들어냈습니다. 이것이 대리자의 핵심입니다.

3)대리자는 언제 사용되나요?

대리자의 진정한 힘은 메서드의 인자로 다른 메서드를 전달할 때 드러납니다.
이를 통해 특정 조건이 만족했을 때 나중에 호출될 메서드,
즉 콜백(Callback) 메서드를 구현할 수 있습니다.
콜백(Callback)은 비동기 프로그래밍에서 핵심적인 역할을 합니다.
이제 계산 결과를 다양한 방식으로 '보고'하는 기능을 만들어 봅시다.

[코드]

// 보고 방식을 정의할 대리자
public delegate int Calculate(int a, int b);
public delegate void Report(string message);

public class Calculator
{
    // 보고 방식을 메서드로 정의
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Subtract(int a, int b)
    {
        return a - b;
    }

    public void ReportToConsole(string message)
    {
        Console.WriteLine($"[콘솔 보고] {message}");
    }

    public void ReportToFile(string message)
    {
        File.WriteAllText("보고서.txt", message);
        Console.WriteLine($"[파일 보고] {message}");
    }

    // 계산을 수행하고, 그 결과를 '보고'하는 역할까지 하는 메서드
    // 두 번째 인자로 '어떻게 보고할지(Report)'에 대한 '기능'을 전달받음!
    public void Perform(int a, int b, Calculate calcMethod, Report reportMethod)
    {
        int result = calcMethod(a, b);
        reportMethod($"계산 결과는 {result} 입니다.");
    }
}

class Program
{
    static void Main()
    {
        Calculator calculator = new Calculator();

        // 1. 덧셈을 하고, 그 결과를 '콘솔'에 보고해줘!
        calculator.Perform(20, 10, calculator.Add, calculator.ReportToConsole);

        // 2. 뺄셈을 하고, 그 결과를 '파일'에 보고해줘!
        calculator.Perform(20, 10, calculator.Subtract, calculator.ReportToFile);
    }
}

[실행 결과]

[콘솔 보고] 계산 결과는 30 입니다.
[파일 보고] 계산 결과는 10 입니다.

[실행 결과: 보고서.txt]

계산 결과는 10 입니다.

Perform메서드는 자신이 어떤 계산을 하는지,
그 결과를 어떻게 보고해야 하는지 전혀 알지 못합니다.
그저 외부에서 주입된 '기능(대리자)'을 실행할 뿐입니다.
대리자를 사용하면 코드는 유연해지고 각 기능의 책임이 명확하게 분리됩니다.

4)정리

대리자(Delegate)는 메서드를 값처럼 다룰 수 있게 해주는 C#의 강력한 기능입니다.

개념설명
대리자(Delegate)메서드에 대한 참조를 담는 타입
타입 안전성대리자에 정의된 서명과 일치하는 메서드만 할당 가능
핵심 용도메서드를 다른 메서드의 인자로 전달 (콜백, 이벤트 핸들러 등)
profile
🚀 미래의 엔지니어를 꿈꾸는 훈련생의 기록 📝

0개의 댓글