한동안은 특별한 주제가 없다면 매일 답변하고 있는 면접 질문에 대해 TIL을 작성할까 합니다.
다른 메서드를 가리키는 참조 타입입니다. Delegate 키워드를 사용하면 메서드를 참조형 변수처럼 다룰 수 있습니다. 이를 통해 동적으로 메서드 호출을 처리할 수 있습니다.
public class Test : MonoBehaviour
{
delegate void TestDelegate();
void Start()
{
TestDelegate test = new TestDelegate(Print);
test();
}
void Print()
{
Debug.Log("델리게이트가 호출되었습니다.");
}
}
다음과 같이 매개변수도 넣어줄 수 있습니다.
public class Test : MonoBehaviour
{
delegate void TestDelegate(int num);
void Start()
{
TestDelegate test = new TestDelegate(Print);
test(7);
}
void Print(int num)
{
Debug.Log($"숫자 : {num}");
}
}
또한 Delegate Chain을 활용해서 한 Delegate로 여러 메서드를 호출할 수 있습니다.
public class Test : MonoBehaviour
{
delegate void TestDelegate();
void Start()
{
TestDelegate test = new TestDelegate(Move);
test += Rotate();
test += Attack();
test();
}
void Move()
{
Debug.Log("플레이어가 이동합니다.");
}
void Rotate()
{
Debug.Log("플레이어가 회전합니다.");
}
void Attack()
{
Debug.Log("플레이어가 공격합니다.");
}
}
이벤트는 작업 실행을 알리기 위해 보내는 메시지입니다. 버튼 클릭과 같은 사용자 조작, 필드 값 변경 등이 일어났을 때, 어떤 변경이 필요한 구독자(외부 클래스의 함수)에게 이 사실을 알리는 데에 사용합니다.
이벤트 핸들러를 이용해서 앞서 말씀드린 버튼 클릭, 필드 값 변경 등의 이벤트를 감지합니다. 다음과 같이 두 가지 방법으로 이벤트 핸들러를 사용할 수 있습니다.
// 이벤트(this, EventArgs.Empty)
public event EventHandler OnSpacePressed;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (OnSpacePressed != null)
{
OnSpacePressed(this, EventArgs.Empty);
}
}
}
// Invoke 메서드
public event EventHandler OnSpacePressed;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
OnSpacePressed?.Invoke(this, EventArgs.Empty);
}
}
그리고 다음과 같이 이벤트 핸들러에 메서드를 등록(구독)하여 이벤트가 발생했을 때, 특정 메서드를 호출할 수 있도록 할 수 있습니다.
void Start()
{
OnSpacePressed += Test; // 구독
// OnSpacePressed -= Test // 구독 취소
}
void Test(object sender, EventArgs eventArgs)
{
Debug.Log($"{sender}의 스페이스바");
}
object는 이벤트를 발생시킨 객체이고 EventArg는 이벤트와 관련된 데이터가 담기는 타입입니다.
Delegate의 한 종류라고 생각하면 이해가 편할 것 같습니다. 메서드를 참조 형식으로 사용할 수 있게 해주는 키워드입니다. 다만, Action 키워드는 반환값이 없는 메서드만 사용할 수 있습니다.
public class Test : MonoBehaviour
{
Action test;
void Start()
{
test += Move;
test += Rotate;
test += Attack;
test();
}
void Move()
{
Debug.Log("플레이어가 이동합니다.");
}
void Rotate()
{
Debug.Log("플레이어가 회전합니다.");
}
void Attack()
{
Debug.Log("플레이어가 공격합니다.");
}
}
매개변수가 필요한 메서드를 Action으로 활용하고 싶다면 다음과 같이 사용하면 됩니다. Action에 매개변수가 필요한 메서드를 등록하려면 람다식을 사용해야 합니다.
Action<int> test;
Action<int, float, bool> testAction;
test += () => Print(7);
testAction += () => TestFunction(7, 5.2, true);
Func 키워드는 Action 키워드와 비슷하지만 반환값이 있는 메서드에 사용한다는 것만 다릅니다.
public class Test : MonoBehaviour
{
Func<bool> test;
private void Start()
{
test += IsTrue;
test += IsFalse;
if (test())
{
Debug.Log("Func is True");
}
}
bool IsTrue()
{
return true;
}
bool IsFalse()
{
return false;
}
}
Func 키워드는 매개변수가 필요한 메서드에 사용하려면 다음과 같이 선언해주어야 합니다. 메서드를 등록하는 방식은 Action 키워드와 같이 람다식을 활용합니다.
Func<int, float, bool> test; //마지막에 있는 bool은 반환값, 그 앞의 값들은 매개변수
네 키워드는 모두 선언한 필드에 메서드가 등록이 되어 있어야 사용이 가능합니다.
만약 등록이 되지 않은 채로 네 키워드로 선언한 필드를 호출하고자하면 Null reference 오류가 발생할 것입니다. 이를 방지하기 위한 것이 Nullable 형식입니다.
어떤 필드를 호출할 때, ?를 붙여서 호출하면 null이 아니면 호출해줍니다.
Func<bool> testFunc;
Action testAction;
void Start()
{
testFunc?.Invoke();
testAction?.Invoke();
}