C#을 일정 수준 이상 사용하다 보면 반드시 마주치게 되는 개념들이 있다. 바로 Reflection, Delegate, Event이다.
이 세 가지는 단순한 문법 요소가 아니라, 프레임워크 설계, 라이브러리 개발, DI 컨테이너, ORM, UI 프레임워크의 핵심 기반 기술이다.
1️⃣ Reflection 이란?
Reflection은 실행 중(Runtime)에 타입 정보를 동적으로 조회하고 조작할 수 있는 기능이다.
즉, 컴파일 시점에 타입을 알지 못해도,
- 클래스 이름
- 메서드 목록
- 프로퍼티
- 필드
- 어트리뷰트
등을 런타임에 분석할 수 있다.
Reflection이 필요한 이유
- 플러그인 구조 구현
- DI 컨테이너 내부 동작
- ORM (Entity Framework)
- Serializer / Mapper
- 테스트 프레임워크
Reflection 기본 예제
using System;
using System.Reflection;
class Sample
{
public int Number { get; set; }
public void Print() => Console.WriteLine("Hello Reflection");
}
class Program
{
static void Main()
{
Type type = typeof(Sample);
Console.WriteLine(type.FullName);
foreach (MethodInfo method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
}
위 코드는 Sample 타입의 메서드 정보를 런타임에 조회한다. 이러한 방식은 프레임워크 내부에서 매우 빈번하게 사용된다.
객체 동적 생성
Type type = typeof(Sample);
object instance = Activator.CreateInstance(type);
이 방식은 클래스 이름만 문자열로 알고 있어도 객체를 생성할 수 있다는 점에서 플러그인 아키텍처의 핵심이다.
📘 Microsoft 공식 문서: Reflection 개요 - Microsoft Docs
.NET의 리플렉션
.NET에서 리플렉션을 검토합니다. 로드된 어셈블리 및 클래스, 인터페이스, 구조체 및 열거형과 같이 그 안에 정의된 형식에 대한 정보를 가져옵니다.
learn.microsoft.com
2️⃣ Delegate (대리자)
Delegate는 메서드를 변수처럼 다룰 수 있게 해주는 타입이다.
C#에서 Delegate는 함수 포인터의 안전한 형태라고 볼 수 있으며, 이벤트, 콜백, 비동기 처리의 핵심 요소이다.
Delegate 기본 구조
delegate int Calculate(int a, int b);
class Program
{
static int Add(int x, int y) => x + y;
static void Main()
{
Calculate calc = Add;
Console.WriteLine(calc(3, 4));
}
}
Delegate는 메서드 시그니처(반환형 + 파라미터)가 동일한 메서드만 참조할 수 있다.
Multicast Delegate
Calculate calc = Add;
calc += (a, b) => a * b;
int result = calc(2, 3);
여러 메서드를 연결할 수 있으며, 호출 순서는 등록 순서이다. 반환값은 마지막 메서드의 결과만 유지된다.
Func / Action
C#에서는 Delegate 선언을 줄이기 위해 Func, Action을 제공한다.
Func<int, int, int> add = (a, b) => a + b;
Action log = msg => Console.WriteLine(msg);
📘 Microsoft 공식 문서: Delegate 개요 - Microsoft Docs
C#에서 대리자 형식을 사용하는 방법 - C#
C#에서 대리자 형식을 탐색합니다. 대리자는 정의된 매개 변수 목록 및 반환 형식이 있는 메서드를 참조하는 날짜 형식입니다. 대리자를 사용하여 메서드를 다른 메서드에 인수로 전달합니다.
learn.microsoft.com
3️⃣ Event (이벤트)
Event는 Delegate를 외부에서 직접 호출하지 못하도록 캡슐화한 구조이다.
즉, 이벤트는:
- 구독 가능
- 발생은 클래스 내부에서만
Event 기본 예제
class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke();
}
}
class Program
{
static void Main()
{
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("버튼 클릭됨");
btn.Click();
}
}
EventHandler 패턴
public event EventHandler OnChanged;
이 패턴은 .NET 표준 이벤트 설계 방식이며, WPF, WinForms, ASP.NET 내부에서도 동일하게 사용된다.
📘 Microsoft 공식 문서: .NET 이벤트 개요 - Microsoft Docs
이벤트 처리 및 이벤트 발생시키기 - .NET
대리자 모델을 기반으로 하는 .NET 이벤트를 처리하고 발생시키는 방법을 알아봅니다. 이 모델을 사용하면 구독자가 공급자에 등록하거나 공급자로부터 알림을 받을 수 있습니다.
learn.microsoft.com
4️⃣ Reflection + Delegate + Event의 조합
실무에서는 이 세 가지가 결합되어 사용된다.
- Reflection → 메서드 탐색
- Delegate → 메서드 바인딩
- Event → 외부 알림
Reflection으로 Event 자동 연결
foreach (var method in type.GetMethods())
{
if (method.Name.StartsWith("On"))
{
Delegate d = Delegate.CreateDelegate(typeof(Action), instance, method);
d.DynamicInvoke();
}
}
이런 구조는 프레임워크 자동 바인딩의 핵심 로직이다.
5️⃣ 성능 및 주의사항
- Reflection은 느리다 → 캐싱 필수
- Delegate 체인은 디버깅이 어렵다
- Event 미해제 시 메모리 누수 발생
- WeakEvent 패턴 고려
6️⃣ 마무리
Reflection, Delegate, Event는 단순 문법이 아니라 .NET 생태계 전체를 이해하는 열쇠이다.
이 개념들을 이해하면?
- 프레임워크 내부 구조 이해
- 라이브러리 설계 능력 향상
- 면접 고급 질문 대응 가능
이라는 강력한 장점을 얻게 된다.