Study/C#

C# 8편: 델리게이트와 이벤트로 동적 프로그래밍 제어하기

wawManager 2024. 10. 27. 12:00
728x90

1. 델리게이트(Delegate)란?

델리게이트는 C#에서 메서드를 참조하는 형식입니다. 즉, 델리게이트는 메서드를 가리키는 포인터 역할을 하며, 해당 메서드를 나중에 호출할 수 있도록 도와줍니다. 델리게이트는 특히 이벤트 기반 프로그래밍에서 자주 사용됩니다. 이를 통해 코드의 유연성을 높이고, 실행 시점에 메서드를 동적으로 바꿀 수 있습니다.

델리게이트 선언

델리게이트는 특정 메서드 시그니처를 기반으로 선언되며, 그 시그니처와 일치하는 메서드라면 델리게이트를 통해 참조할 수 있습니다.

delegate int Operation(int x, int y); // 두 개의 int 매개변수를 받아 int 값을 반환하는 델리게이트

델리게이트를 사용한 예시

using System;

class Program
{
    // 델리게이트 선언
    delegate int Operation(int x, int y);

    // 메서드들
    static int Add(int a, int b) => a + b;
    static int Multiply(int a, int b) => a * b;

    static void Main(string[] args)
    {
        // 델리게이트 인스턴스 생성
        Operation op = Add;
        Console.WriteLine("덧셈: " + op(5, 3)); // 출력: 8

        // 델리게이트에 다른 메서드 할당
        op = Multiply;
        Console.WriteLine("곱셈: " + op(5, 3)); // 출력: 15
    }
}

위 코드에서 Operation 델리게이트는 Add와 Multiply 메서드를 가리킬 수 있습니다. 델리게이트를 통해 메서드를 호출할 수 있으며, 동적으로 다른 메서드로 바꿀 수 있습니다.

2. 델리게이트 체인(Delegate Chaining)

델리게이트는 여러 메서드를 연결하여 한 번에 호출할 수 있습니다. 이를 델리게이트 체인이라고 하며, += 연산자를 통해 여러 메서드를 델리게이트에 추가할 수 있습니다.

델리게이트 체인 예시

using System;

class Program
{
    delegate void Notify(); // 반환값이 없는 델리게이트

    static void PrintMessage1() => Console.WriteLine("첫 번째 메시지");
    static void PrintMessage2() => Console.WriteLine("두 번째 메시지");

    static void Main(string[] args)
    {
        Notify notify = PrintMessage1;
        notify += PrintMessage2; // 델리게이트 체인에 메서드 추가

        notify(); // 출력: 첫 번째 메시지, 두 번째 메시지
    }
}

여기서 notify 델리게이트는 PrintMessage1과 PrintMessage2 메서드를 모두 호출하게 됩니다.

3. 델리게이트와 람다(Lambda) 표현식

델리게이트를 사용할 때, 람다 표현식을 이용하여 더 간단하게 메서드를 정의할 수 있습니다. 람다 표현식은 익명 메서드로서, 간결하게 델리게이트에 할당할 수 있습니다.

람다 표현식을 사용한 델리게이트

using System;

class Program
{
    delegate int Operation(int x, int y);

    static void Main(string[] args)
    {
        Operation add = (a, b) => a + b;
        Operation multiply = (a, b) => a * b;

        Console.WriteLine("람다로 덧셈: " + add(5, 3)); // 출력: 8
        Console.WriteLine("람다로 곱셈: " + multiply(5, 3)); // 출력: 15
    }
}

4. 이벤트(Event)란?

이벤트는 C#에서 특정 작업이 발생했을 때 이를 알려주는 메커니즘입니다. 이벤트는 주로 델리게이트를 기반으로 구현되며, 객체 간의 상호작용을 비동기적으로 처리하는 데 유용합니다. 예를 들어, 버튼 클릭과 같은 UI 이벤트나 데이터 변경 등의 상황을 처리할 때 사용됩니다.

이벤트 선언

이벤트는 event 키워드를 사용하여 선언되며, 이벤트에 대한 처리는 델리게이트를 통해 이루어집니다.

delegate void NotifyEventHandler(string message); // 델리게이트 선언

class Publisher
{
    public event NotifyEventHandler OnNotify; // 이벤트 선언

    public void TriggerEvent(string message)
    {
        if (OnNotify != null)
        {
            OnNotify(message); // 이벤트 발생
        }
    }
}

이벤트 구독(Subscribe)

이벤트는 구독(Subscribe) 방식으로 작동합니다. 즉, 이벤트가 발생하면 이를 구독한 메서드들이 호출됩니다.

using System;

class Program
{
    static void Main(string[] args)
    {
        Publisher publisher = new Publisher();

        // 이벤트 구독
        publisher.OnNotify += DisplayMessage;

        // 이벤트 발생
        publisher.TriggerEvent("이벤트가 발생했습니다!");
    }

    static void DisplayMessage(string message)
    {
        Console.WriteLine(message);
    }
}

여기서 OnNotify 이벤트가 발생하면 DisplayMessage 메서드가 호출되어 메시지를 출력합니다.

5. 실습 예제: 간단한 이벤트 시스템 구현

이제 델리게이트와 이벤트를 활용하여 간단한 시스템을 구현해 보겠습니다. 버튼을 클릭하는 상황을 가정하고, 버튼 클릭 시 이벤트가 발생하도록 설정하겠습니다.

버튼 클릭 이벤트 구현 예시

using System;

class Button
{
    // 버튼 클릭 이벤트를 위한 델리게이트와 이벤트 선언
    public delegate void ButtonClickEventHandler(string message);
    public event ButtonClickEventHandler OnClick;

    // 버튼 클릭 메서드
    public void Click()
    {
        if (OnClick != null)
        {
            OnClick("버튼이 클릭되었습니다.");
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        Button button = new Button();

        // 버튼 클릭 이벤트 구독
        button.OnClick += ShowMessage;

        // 버튼 클릭 시도
        button.Click();
    }

    static void ShowMessage(string message)
    {
        Console.WriteLine(message);
    }
}

이 코드에서는 Button 클래스가 클릭 이벤트를 발생시키고, ShowMessage 메서드가 그 이벤트를 구독하여 처리합니다. 프로그램을 실행하면 "버튼이 클릭되었습니다." 메시지가 출력됩니다.

6. 이벤트 처리에서의 주의사항

이벤트를 처리할 때는 다음 사항들을 고려해야 합니다.

  • Null 검사: 이벤트가 구독된 메서드가 없을 경우 null일 수 있으므로, 이벤트 발생 전에는 null 검사를 해야 합니다.
  • 이벤트 구독 해제: 더 이상 필요하지 않은 이벤트 구독은 반드시 해제해야 메모리 누수를 방지할 수 있습니다. 이벤트 구독 해제는 -= 연산자를 사용합니다.

이벤트 구독 해제

button.OnClick -= ShowMessage;

7. Action 및 Func 델리게이트

C#에서는 미리 정의된 델리게이트인 ActionFunc를 자주 사용합니다. 이를 통해 델리게이트 선언을 생략할 수 있습니다.

  • Action: 반환값이 없는 델리게이트
  • Func: 반환값이 있는 델리게이트

Action 예시

Action<string> printMessage = message => Console.WriteLine(message);
printMessage("Action 델리게이트 사용");

Func 예시

Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine("Func 델리게이트 사용: " + add(5, 3)); // 출력: 8

8. 실습 예제: 여러 이벤트 처리

이번 예제에서는 버튼에 두 개 이상의 메서드를 구독하고, 버튼 클릭 시 각각 다른 동작을 수행하도록 해보겠습니다.

using System;

class Button
{
    public delegate void ButtonClickEventHandler(string message);
    public event ButtonClickEventHandler OnClick;

    public void Click()
    {
        if (OnClick != null)
        {
            OnClick("버튼 클릭 이벤트 발생");
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        Button button = new Button();

        // 여러 메서드 구독
        button.OnClick += ShowMessage;
        button.OnClick += LogMessage;

        // 버튼 클릭
        button.Click();
    }

    static void ShowMessage(string message)
    {
        Console.WriteLine($"메시지 출력: {message}");
    }

    static void LogMessage(string message)
    {
        Console.WriteLine($"로그 기록: {message}");
    }
}

이 프로그램을 실행하면 버튼 클릭 시 두 메서드가 각각 호출되어 "메시지 출력"과 "로그 기록"이 동시에 실행됩니다.

9. 요약

  • 델리게이트는 메서드를 참조하고, 나중에 호출할 수 있는 메커니즘입니다. 델리게이트는 메서드의 서명을 기반으로 합니다.
  • 이벤트는 델리게이트를 사용하여 특정 작업이 발생할 때 이를 구독한 메서드들에게 알릴 수 있는 기능입니다.
  • ActionFunc는 자주 사용하는 델리게이트 형식을 미리 정의한 것으로, 델리게이트 선언을 줄여줍니다.
  • 이벤트 구독 해제를 통해 메모리 누수를 방지할 수 있습니다.

다음 편에서는 C#의 LINQ를 사용하여 데이터를 처리하는 방법을 학습하겠습니다.

728x90