[로봇활용_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개의 댓글