delegate (대리자), Event
delegate 대리자, 말 그대로 직접호출하는 대신 특정한 때에, 내가 넘겨준 함수를 실행해주세요. 하고 맡기는 것이다.
delegate 는 함수자체를 형식으로 지정하고 이를 매개변수로 넘겨줄 수 있다.
delegate string InfoPrint();
void PrintInfo(InfoPrint info)
{
info();
}
string TestDelegate()
{
Console.WriteLine("Info");
return "Info";
}
main()
{
InfoPrint ip = new InfoPrint(TestDelegate);
ip();
PrintInfo(TestDelegate);
}
먼저 delegate 형식을 선언한다. 함수자체를 형식으로 만드는 것이기에 반환형 string에 이름 InfoPrint, 입력 없음으로 delegate 형식을 만든 것이다.
먼저 PrintInfo 함수를 보면 매개변수로 delegate 를 받는 모습이다. info() 방식으로 실행할 수 있다.
그리고 delegate에 넣어줄 함수는 TestDelegate처럼 반환형, 매개변수를 맞춰야한다.
main에서 보면 객체를 생성하듯 new로 만들 수 있다.
또는 직접 PrintInfo를 호출하며 delegate를 지정할수도 있다.
그런데 첫번째 객체방식으로 생성하면 이점이 하나 있다.
delegate string InfoPrint();
void PrintInfo(InfoPrint info)
{
info();
}
string TestDelegate()
{
Console.WriteLine("Info");
return "Info";
}
string TestDelegate2()
{
Console.WriteLine("Info2");
return "Info2";
}
main()
{
InfoPrint ip = new InfoPrint(TestDelegate);
ip+=TestDelegate2;
ip();
PrintInfo(TestDelegate);
}
위 main 에서처럼 delegate 형식에는 하나의 함수만 지정할 수 있는게 아니라 체이닝하여 여러개의 함수가 실행되도록 할 수도 있다. ip()가 실행되면 Info, Info2 가 차례로 출력된다.
근데 여기서 PrintInfo에서만 info 가 호출되게 하고 싶지만 현재는 어디에서든 호출을 할 수가 있다.
만약 info가 다른데서 호출됐을 때, 아주 큰 리스크가 있는 것이라면 위험한 설계가 될 수 있다.
그래서 Event라는 것을 사용한다.
간단히 말하면 delegate를 한 번 더 매핑한다.
delegate string InfoPrint();
event InfoPrint InfoEvent;
이렇게 매핑을 한다. 그리고 똑같이 InfoEvent에 += 으로 체이닝하거나 -=으로 제거할 수 있다.
다만 event가 생성된 클래스 내에서만 InfoEvent() 가 가능하다는 점에서 캡슐화할 수 있다.
또한 C#에는 Func와 Action 같은 이미 만들어진 delegate들이 있다.
둘다 제너릭으로 만들어진 델리게이트인데, Func는 반환형이 있을 때, Action은 없을 때 사용하면 된다.