Callback
함수함수를 직접 호출하는 것이 아닌, 다른 함수의 매개변수로 참조되어 참조한 함수가 호출될 때 내부에서 매개변수로써 호출되는 것
콜백함수를 간단히 말하면 다른 함수의 매개변수로 참조되어, 간접호출로써 실행되는 것을 의미한다. 이때 매개변수로 참조되는 방식은 익명 메서드, 화살표 함수, 델리게이트 등 여러가지가 있다. 먼저 Button
이라는 클래스에 콜백함수를 호출하기 위한 메서드를 선언해보자.
public class Button
{
public void Click(Action callback)
{
if (callback != null) callback();
}
}
위와 같이 Click
메서드에 일반화 델리게이트 Action
을 활용하여 콜백함수를 매개변수로 지정하고, 내부에서 콜백함수의 호출 조건을 작성하면 된다.
static void Main(string[] args)
{
Button anonyButton = new Button();
anonyButton.Click(delegate { Console.WriteLine("익명"); });
}
익명메서드를 활용하면 매개변수에 콜백함수를 이름이 없는 함수로 지정하는 것이 가능하며, 이를 조금 더 간편하게 사용하기 위해 줄인 것이 화살표 함수이다.
static void Main(string[] args)
{
Button arrowButton = new Button();
arrowButton.Click(() => { Console.WriteLine("화살표"); });
}
빈괄호 안에 들어가는 것은 콜백함수의 매개변수로 위에서 사용된 콜백함수는 반환형이 void이기에 비워둔 것이다.
public class File
{
public void Save() => Console.WriteLine("저장");
public void Load() => Console.WriteLine("불러오기");
}
static void Main(string[] args)
{
File file = new File();
Button saveButton = new Button();
Button loadButton = new Button();
saveButton.Click(file.Save);
loadButton.Click(file.Load);
}
위처럼 특정 클래스의 멤버함수를 포인터로써 매개변수로 지정하는 것이 가능하며, 간단하게 콜백함수의 기능을 구현할 수 있다.
회사에서 면접자가 면접관의 호출에 의해 들어가는 상황을 구현해보자
public class Interviewer
{
public void Call(Action Secretary)
{
Console.WriteLine("면접관이 호출하다");
Secretary();
}
}
public class Company
{
private Interviewer _interviewer;
private Interviewee _interviewee;
public Company()
{
_interviewer = new();
_interviewee = new();
}
public void Interview()
{
_interviewer.Arrive(_interviewee.Receive);
}
}
public class Interviewee
{
public void Receive()
{
Console.WriteLine("면접 희망자가 호출을 받다");
}
}
internal class Program
{
static void Main(string[] args)
{
Company company = new Company();
company.Interview();
}
}
면접관이 호출하다
면접 희망자가 호출을 받다
콜백함수는 동기적인 상황에서 어떤 작업의 결과를 전달하는 방식으로 사용된다. 위에서 사용된 코드를 예시로 들자면, 면접관이 Call
한 결과가 면접자의 receive
로 전달된 것이다. 물론 동기적인 상황에서는 단순히 코드를 순서대로 나열하는 것으로 같은 상황을 연출할 수 있으니, 콜백함수의 사용의의에 대해 의문이 생길 것이다.
콜백함수의 진정한 의의는 비동기적인 상황에서의 활용으로부터 나온다. C#에서 콜백은 보통 델리게이트를 사용하여 구현되는데, 이 경우 메서드에 대한 안전한 참조를 가능하게 해주며 비동기적인 상황에서의 결과를 전달해주는데 효과적이다.
지금은 스레드(Thread)의 개념을 제대로 알지 못하기에 콜백함수의 의의는 스레드와 BeginInvoke에 대해 공부할 때 연관지어 다시 작성해보겠다.
C#에서 콜백은 델리게이트를 사용하여 구현한다고 했는데, 그 이유는 클래스 간의 결합도를 낮추는 방법이기 때문이다. 콜백은 클래스의 직접적인 참조로도 구현이 가능한데, 아래의 예시를 보자.
public class Interviewer
{
private Interviewee interviewee;
public Interviewer()
{
interviewee = new Interviewee();
}
public void Arrive()
{
Console.WriteLine("면접관이 도착하다");
interviewee.Receive();
}
}
public class Company
{
private Interviewer _interviewer;
public Company()
{
_interviewer = new();
}
public void Interview()
{
_interviewer.Arrive();
}
}
public class Interviewee
{
public void Receive()
{
Console.WriteLine("면접 희망자가 호출을 받다.");
}
}
internal class Program
{
static void Main(string[] args)
{
Company company = new Company();
company.Interview();
}
}
면접관이 호출하다
면접 희망자가 호출을 받다
Interviewer
클래스 내부에 Interviewee
클래스를 참조하여 Arrive
함수 내부에 직접 interviewee
의 Recieve
를 넣었다.
물론 델리게이트를 사용해 구현한 것과 동일한 결과가 나오는 것을 확인할 수 있지만, 이 경우 클래스 간의 결합도가 높아지게 된다.
클래스 내부에서 다른 클래스 인스턴스를 생성할 경우 결합도가 높다고 말하기에, 델리게이트를 활용하여 콜백함수를 구현하는 것이 클래스 간 결합도를 낮추는데 도움이 될 것이다.