델리게이트란 무엇인가?
델리게이트(대리자)는 메서드를 참조를 나타내는 형식이다. 다시 말해 델리게이트의 포인터는 메서드의 주소를 가리킨다. 그러나 ms공식 문서에 따르면 c++ 포인터와는 다르게 객체지향이라서 인스턴스 및 메서드를 모두 캡슐화한다고 되어있다. 델리게이트는 주로 메서드를 다른 메서드의 매개변수로 전달하는 데에 쓰인다.
대리자는 크게 다음과 같은 역할을 수행한다.
- 메서드를 메서드의 매개변수로 전달할 수 있다.
- 콜백 메소드를 정의할 수 있다.
- 여러 개의 대리자를 연결하여 실행할 수 있다. 델리게이트 체인(delegate chain)이라고도 한다.
그리고 이러한 기능은 람다식을 사용하여 간단하게 정의가 가능하다.
왜 사용하는가?
가장 큰 이유는 위에서 언급한 것처럼 메서드 자체를 매개변수로 전달하기 위해서이다.
그러나 이 외에도 event와 함께 사용하여 콜백 함수로 사용할 수 있고 delegate chain을 통해 여러 함수를 한 번에 호출하는 데에 사용할 수 있다.
어떻게 사용할 수 있는가?
다음 예제 소스코드를 보자.
public class Test0202 : MonoBehaviour
{
//델리게이트의 기본형태이다.
public delegate void JDelegate(int a, string b);
void Start()
{
//델리게이트에 참조할 함수를 넣어서 인스턴스화 한다.
JDelegate j1 = PrintBisA;
//만약 함수가 일회성이라면 무명메소드를 사용하여 작성할 수 있다.
JDelegate j2 = delegate (int _a, string _b) { Debug.Log(_b + " is " + _a); };
//람다식을 사용하면 더 간편하게 사용이 가능하다.
JDelegate j3 = (int a1, string b1) => Debug.Log(a1 + " from " + b1);
//델리게이트 체인을 통해서 여러 함수를 동시에 실행할 수 있다.
j2 += PrintBisA;
j2.Invoke(3, "Cool");
}
public void PrintBisA(int _a, string _b)
{
Debug.Log(_b + " is " + _a);
}
}
델리게이트는 함수의 주소를 참조하기 때문에 반드시 함수를 넣어주어야 한다.
만약 한번 사용하고 버릴 함수라면 무명 메서드를 사용할 수도 있다. 그러나 이것마저 줄여서 람다식으로 정의가 가능하다.
델리게이트 체인은 += 연산자로 추가할 수 있으며, 연결된 함수들을 모두 실행한다. 이때 Invoke 함수를 사용하게 되면 마지막으로 추가된 함수만 반환 값을 가져서 반환한다.
유니티에서 어떻게 활용할 수 있는가?
우리는 델리게이트가 매개변수로써 활용되고 결과적으로는 함수의 역할을 한다는 것을 이용해야 한다. 함수는 매개변수가 있고 특정 반환 값이 존재한다. 다시 말해서 델리게이트(대리자)를 사용하면 특정 타입의 반환 값을 자유롭게 매개변수로 전달할 수 있다는 것이다.
다음 예시를 살펴보자.
public class Diary
{
public string title;
public int month;
public int day;
public int temperature;
public int humidity;
public int moneySpent;
}
해당 클래스는 하루 일기의 정보를 담고 있다. 제목을 제외하면 int가 대부분인데 만약 배열이나 리스트로 저장된 일기의 필드 정보를 빼내 오려면 어떻게 해야 할까?
다음은 일기의 각 필드의 최고값을 가져오는 예제이다.
private string GetHighestSpentInDiary(List<Diary> diary)
{
int maxValue = 0;
string title = "";
for (int i = 0; i < diary.Count; i++)
{
if (diary[i].moneySpent > maxValue)
{
maxValue = diary[i].moneySpent;
title = diary[i].title;
}
}
return title;
}
private string GetHighestTemperatureInDiary(List<Diary> diary)
{
int maxValue = 0;
string title = "";
for (int i = 0; i < diary.Count; i++)
{
if (diary[i].temperature > maxValue)
{
maxValue = diary[i].temperature;
title = diary[i].title;
}
}
return title;
}
//...
이렇게 하면 아주 간단하게 각 필드의 최고값에 해당하는 title을 가져올 수 있다. 그러나 너무 비효율적이다...
함수의 재사용성이라고는 찾아볼 수 없다. 이때 delegate를 사용하면 된다.
public List<Diary> myDiary = new List<Diary>();
public delegate int DiaryDelegate(Diary perDay);
private string GetHighestValueInDiary(List<Diary> diary, DiaryDelegate diaryDelegate)
{
int maxValue = 0;
string title = "";
for (int i = 0; i < diary.Count; i++)
{
if (diaryDelegate(diary[i]) > maxValue)
{
maxValue = diaryDelegate(diary[i]);
title = diary[i].title;
}
}
return title;
}
public void PrintHottestDay()
{
Debug.Log(GetHighestValueInDiary(myDiary, a => a.temperature));
}
public void PrintHumiditestDay()
{
Debug.Log(GetHighestValueInDiary(myDiary, a => a.humidity));
}
대리자를 매개변수로 넣어주게 되면 얻고자 하는 필드 변수를 람다식으로 넣어주기만 하면 된다.
대리자를 사용하면 함수를 여러 개 만드는 방법보다 더 뛰어난 재사용성을 갖는다.
'C# 프로그래밍 > 문법 개념' 카테고리의 다른 글
[C#] 인터페이스 (Interface) (0) | 2022.05.05 |
---|---|
[C#] 액션과 펑션 (Action & Func) (0) | 2022.05.05 |
[C#] 직렬화 (Serialization) (0) | 2022.05.03 |
[C#] 구조체와 클래스 (Struct & Class) (0) | 2022.05.03 |
[C#] 상수 선언과 읽기 전용 키워드 (Const & Read only) (0) | 2021.08.19 |