C#에서는 delegate를 사용하는 과정에서 독립성을 제공하기 위해 event라는 기능을 제공한다.
event를 사용하면 delegate를 대입 연산자로 초기화할 수 없고 +=이나 -=으로만 메소드를 등록할 수 있게 해준다. 아래 코드는 event를 사용하는 예제 코드이다.
delegate void HANDLER();
class Button
{
public HANDLER handler = null;
public void press()
{
handler?.Invoke();
}
}
class Program
{
public static void Main(string[] args)
{
Button btn = new Button();
btn.handler = F1;
// 1)
btn.handler += F2;
// 2)
btn.handler = F2; // F2만 출력
btn.press();
}
public static void F1() { Console.WriteLine("F1"); }
public static void F2() { Console.WriteLine("F2"); }
}
delegate를 버튼의 handler로 사용하는 상황에서 2개 이상의 delegate를 등록할 때 1번과 같이 +=으로 추가하면 두 메소드가 호출되지만 2번과 같이 대입 연산자를 사용한다면 이전에 등록된 delegate가 사라지게 된다.
이를 방지하기 위해 아래와 같이 event 키워드로 delegate를 선언할 수 있다.
public event HANDLER handler = null;
event를 사용하게 되면 delegate를 사용할 때 대입연산자를 사용하지 못하고 +=, -=으로만 handler를 추가할 수 있게 된다.
즉, 특정 메소드를 추가/삭제하는 과정이 다른 메소드에 영향을 미치지 못하게 된다.
public static void Main(string[] args)
{
// ...
btn.handler = F1; // Error!
btn.handler = F2; // Error!
btn.handler += F1; // Ok!
btn.handler += F2; // Ok!
btn.press();
}
event로 선언된 delegate는 컴파일시 컴파일러가 아래와 같은 구조로 변환해서 실행하게 된다.
private HANDLER handler = null;
public void add_handler(HANDLER f) { handler += f; }
public void remove_handler(HANDLER f) { handler -= f; }
그리고, 대입연산자로 함수를 추가하는 부분은 add_handler를 호출하는 구조로 변경되어서 컴파일이 진행된다. 위 코드는 간단하게 표현한 코드이고 실제 내부에는 null 체크 등 여러 확인 과정을 거치고 함수 등록을 진행한다.
C#에는 Action과 Func라는 자주 사용되는 형태의 delegate가 미리 정의되어 있는데 다음과 같은 특징을 가진다.
delegate를 사용할 때 미리 정의된 함수만 사용하는 것이 아닌 람다식을 delegate에 등록할 수 있다.
예를들어, 아래코드에서 f1과 f2는 같은 기능을 하는 delegate이다.
public static int Add(int a, int b) { return a + b; }
public static void Main(string[] args)
{
Func<int, int, int> f1 = Add;
Func<int, int, int> f2 = (int a, int b) => { return a + b; };
}
람다식이 간단할 경우에 식을 간단하게 표현할 수 있는데 아래와 같이 표현할 수 있다. 아래에서 f1 ~ f4는 같은 기능을 하는 delegate이고 f5와 f6은 매개변수가 하나일 때 더 짧게하는 경우를 나타낸 코드이다.
Func<int, int, int> f1 = Add;
Func<int, int, int> f2 = (int a, int b) => { return a + b; };
Func<int, int, int> f3 = (a, b) => { return a + b; };
Func<int, int, int> f4 = (a, b) => a + b;
Func<int, int> f5 = (a) => a + a;
Func<int, int> f6 = a => a + a;