전체 코드
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CSharp
{
internal class InputManager
{
public delegate void OnInputKey();
public event OnInputKey InputKey;
public void Update()
{
if (Console.KeyAvailable == false)
return;
ConsoleKeyInfo info = Console.ReadKey();
if (info.Key == ConsoleKey.A)
{
InputKey?.Invoke();
}
}
}
}
using System.ComponentModel;
using System.Numerics;
using System.Threading;
using System.Collections.Generic;
namespace CSharp
{
class Program
{
public static void OnInputTest()
{
Console.WriteLine("Input Received");
}
static void Main(string[] args)
{
InputManager inputManager = new InputManager();
inputManager.InputKey += OnInputTest;
while (true)
{
inputManager.Update();
}
}
}
}
🔔 이벤트란?
Event는 특정 상황이 발생했을 때,
외부에서 미리 등록해 둔 메서드들을 자동 호출하는 기능이다.
- 내부에서는 이벤트를 발생시키는 역할
- 외부에서는 이벤트에 함수들을 구독(등록)하는 역할
- 옵저버 패턴(Observer Pattern)의 전형적인 구현
💡 이벤트 구조란?
| 구성요소 | 설명 |
|---|
| Delegate | 등록 가능한 함수 시그니처 정의 |
| Event | Delegate 기반으로 이벤트 객체 생성 |
| 등록 (Subscription) | 외부에서 이벤트에 함수 등록 |
| 발생 (Trigger) | 내부에서 특정 조건 시 이벤트 실행 (등록된 함수 호출) |
| 호출 제한 | 이벤트는 클래스 내부에서만 실행 가능 |
📥 이벤트 정의
namespace CSharp_ETC
{
internal class InputManager
{
public delegate void OnInputKey();
public event OnInputKey InputKey;
public void Update()
{
if (Console.KeyAvailable == false)
return;
ConsoleKeyInfo info = Console.ReadKey();
if (info.Key == ConsoleKey.A)
{
InputKey?.Invoke();
}
}
}
}
2️⃣ Program 클래스 (이벤트 구독 및 실행)
namespace CSharp_ETC
{
internal class Program
{
public static void OnInputTest()
{
Console.WriteLine("Input Received");
}
static void Main(string[] args)
{
InputManager inputManager = new InputManager();
inputManager.InputKey += OnInputTest;
while (true)
{
inputManager.Update();
}
}
}
}
🛠️ 예제 흐름 분석
| 단계 | 설명 |
|---|
| ① | OnInputKey 대리자 정의 (void 반환, 파라미터 없음) |
| ② | InputKey 이벤트 정의 (OnInputKey 타입) |
| ③ | 키 입력 감지 (Update) |
| ④ | A 키 눌리면 InputKey 이벤트 발생 (등록된 함수 모두 호출) |
| ⑤ | OnInputTest 함수 정의 (A 눌리면 실행할 함수) |
| ⑥ | 이벤트에 OnInputTest 함수 등록 |
| ⑦ | 무한 루프에서 계속 입력 체크 |
| ⑧ | A 키 누르면 이벤트 발생 → OnInputTest 실행 |
🔗 이벤트 vs 델리게이트 차이점 완벽 정리
| 비교 항목 | Delegate | Event |
|---|
| 역할 | 함수 포인터 저장 및 실행 | 특정 상황 발생 시, 등록된 함수 호출 |
| 외부 호출 | 가능 | 불가능 (내부에서만 호출) |
| 캡슐화 | 약함 | 강함 (은닉성 강화) |
| 등록 방법 | += / -= | += / -= |
| 체이닝 | 가능 | 가능 |
| 옵저버 패턴 | 직접 구현 필요 | 기본 지원 |
⚠️ 이벤트가 필요한 이유
델리게이트의 치명적 문제점
OnClicked clicked = Test;
clicked();
- 외부에서 막 눌러버리면? 데이터 꼬일 위험
- 이벤트는 이 문제 해결 → "호출은 내부만 가능"
이벤트 보호 예시
public event OnInputKey InputKey;
- 외부에서
InputKey() 호출하면 컴파일 에러!
- 오직
InputManager 내부에서만 호출 가능
✅ 이벤트 흐름
1️⃣ 이벤트 정의
public delegate void OnInputKey();
public event OnInputKey InputKey;
2️⃣ 이벤트 발생 (내부)
if (info.Key == ConsoleKey.A)
{
InputKey?.Invoke();
}
3️⃣ 이벤트 구독 (외부)
inputManager.InputKey += OnInputTest;
📦 이벤트 vs 델리게이트 선택 기준
| 상황 | 선택 |
|---|
| 외부에서 직접 호출 가능해야 함 | Delegate |
| 내부에서만 호출하고 외부는 등록만 | Event |
| 버튼 클릭 같은 GUI 이벤트 처리 | Event |
| 콜백 구현 | Delegate/Event 모두 가능 |
📜 이벤트 정리
| 키워드 | 설명 |
|---|
| Delegate | 메서드 참조 포인터 |
| Event | 내부 발생 + 외부 등록 |
| += | 이벤트 구독 |
| -= | 구독 해제 |
| Invoke() | 이벤트 발생 (내부에서만) |
| Observer 패턴 | 이벤트의 핵심 디자인 패턴 |
C# 이벤트 관용 패턴
public event EventHandler<EventArgs> SomethingHappened;
EventHandler<T> 사용하는 방식 추천 (표준 패턴)
- 이벤트 인자 전달 필요할 때 매우 유용