C# 8편: 델리게이트와 이벤트로 동적 프로그래밍 제어하기
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#에서는 미리 정의된 델리게이트인 Action과 Func를 자주 사용합니다. 이를 통해 델리게이트 선언을 생략할 수 있습니다.
- 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. 요약
- 델리게이트는 메서드를 참조하고, 나중에 호출할 수 있는 메커니즘입니다. 델리게이트는 메서드의 서명을 기반으로 합니다.
- 이벤트는 델리게이트를 사용하여 특정 작업이 발생할 때 이를 구독한 메서드들에게 알릴 수 있는 기능입니다.
- Action과 Func는 자주 사용하는 델리게이트 형식을 미리 정의한 것으로, 델리게이트 선언을 줄여줍니다.
- 이벤트 구독 해제를 통해 메모리 누수를 방지할 수 있습니다.
다음 편에서는 C#의 LINQ를 사용하여 데이터를 처리하는 방법을 학습하겠습니다.