위임(Delegate)의 기본 구조
위임은 특정 반환 타입과 매개변수 시그니처를 가진 메서드를 참조할 수 있는 형식이다. 이는 함수 포인터와 유사하지만, 타입 안전성을 갖춘 객체 기반의 구현이다.
public delegate void MessageHandler(string message);
이 위임을 사용해 인스턴스를 생성하고, 적절한 메서드를 연결할 수 있다.
MessageHandler handler = new MessageHandler(DisplayMessage);
private void DisplayMessage(string msg)
{
MessageBox.Show(msg);
}
위임은 Invoke 또는 BeginInvoke를 통해 호출되며, 후자는 비동기 실행을 가능하게 한다.
handler.BeginInvoke("메시지 전달 완료", null, null);
또한 람다 표현식이나 익명 메서드를 활용해 간결하게 위임을 정의할 수 있다.
MessageHandler simpleHandler = (msg) => Console.WriteLine($"처리: {msg}");
simpleHandler("간단한 메시지");
이벤트(Event)의 역할과 설계
이벤트는 위임을 기반으로 한 특수한 형태로, 클래스 내부에서만 발생시키는 것이 원칙이다. 이는 정보 은닉과 안정성 향상을 위해 설계되었다.
public event MessageHandler OnMessageReceived;
외부 코드는 이벤트에 대한 등록만 가능하며, 직접 발행할 수 없다. 예를 들어, 프로세스 종료 시 이벤트를 발생시키는 경우:
process.Exited += (sender, args) => {
Console.WriteLine("프로세스 종료됨");
};
이벤트는 주로 EventHandler<TEventArgs> 또는 커스텀 위임을 통해 정의되며, 발생 시 발생자와 관련된 데이터를 전달할 수 있다.
위임과 이벤트의 차이점
- 위임: 어디서든
Invoke나BeginInvoke로 호출 가능. 자유도가 높지만, 과도한 사용은 결합도 증가로 이어질 수 있음. - 이벤트: 클래스 내부에서만
raise가능. 외부로부터의 무분별한 호출을 방지하여 안정적인 통신 모델을 제공.
비동기 처리와 위임의 관계
위임은 비동기 처리를 위한 핵심 도구 중 하나이다. BeginInvoke는 스레드 풀을 이용해 백그라운드 스레드에서 작업을 수행한다.
handler.BeginInvoke("비동기 메시지", callback => {
Console.WriteLine("작업 완료");
}, null);
이 방식은 직접 Thread나 ThreadPool를 다루는 것보다 더 간편하며, 콜백 기반의 제어 흐름을 제공한다.
Task를 통한 고급 비동기 처리
.NET Framework 4 이후, System.Threading.Tasks.Task는 비동기 작업의 중심이 되었다. 이는 스레드 풀 기반으로 동작하며, 복잡한 의존성 관리와 취소 기능을 지원한다.
Task.Run(() => {
Console.WriteLine($"작업 스레드 ID: {Task.CurrentId}");
// 긴 작업 수행
});
여러 가지 시작 방법이 있으며, TaskFactory.StartNew, new Task(...) 후 Start() 호출 등이 있다.
// TaskFactory 사용
var task1 = TaskFactory.StartNew(() => DoWork());
// Task 생성 후 시작
var task2 = new Task(() => DoWork());
task2.Start();
// 동기 실행 (대기)
task2.RunSynchronously();
TaskCreationOptions를 통해 작업의 특성을 명시할 수 있다. 예를 들어, LongRunning은 장시간 실행 작업임을 알리고, 새 스레드 할당을 요청할 수 있다.
Action과 Func의 활용
제네릭 위임인 Action과 Func는 코드를 더욱 간결하게 만든다.
// Action: 반환 없음, 매개변수 가능
Action<string> print = msg => Console.WriteLine(msg);
print("안녕하세요");
// Func: 반환 있음, 최대 16개의 매개변수까지 지원
Func<int, int, int> add = (a, b) => a + b;
int result = add(5, 3);
Console.WriteLine(result);
이러한 구문은 람다와 함께 사용하면 매우 효율적이고 가독성이 뛰어난 코드를 작성할 수 있다.