델리게이트
- 델리게이트는 int, string 같은 타입
- 여러가지 함수들을 가지고 있을 수 있다. (리스트나 딕셔너리도)
Private delegate void BasicDelegate();
- 반환형이 없는 void 반환형을 들고 있고 매개변수가 없는 함수들만 받을 수 있는 델리게이트 라는 뜻
- 현재는 BasicDelegate();라는 타입을 만든 것
Private delegate void BasicDelegate();
Private BasicDelegate startProcess;
private void Awake()
{
startProcess = SayHI;
}
Void Start()
{
startProcess();
startProcess?.Invoke();
}
public void SayHI()
{
Debug.Log("안녕~");
}
- 이렇게 해주고 플레이 하면 콘솔에 안녕~ 이 뜨게된다.
- SayHI 함수를 델리게이트에 저장해 사용한 것
Private BasicDelegate startProcess;는 다시 선언해 객체를 만든건데, private int a; 와 같다고 생각하면 쉽다.
- BasicDelegate = SayHi;가 안되는 이유는.. 똑같이 int = a라고 하는 것과 같음. 이상함
- delegate는 타입이니까 변수명인 startProcess를 사용해 startProcess = SayHi 라고 쓴 것
Private delegate void BasicDelegate();
Private BasicDelegate startProcess;
private void Awake()
{
startProcess = SayHI;
startProcess = Digimon;
}
Void Start()
{
startProcess();
startProcess?.Invoke();
}
public void SayHI()
{
Debug.Log("안녕~");
}
public void Digimon()
{
Debug.Log("디지몬~");
}
- 이러고 실행하면 디지몬~ 만 뜬다.
- 덮어씌워진 것
string name = "홍";
name = "길동";
Debug.Log(name);
> 출력: 길동
string name = "홍";
name += "길동";
Debug.Log(name);
> 출력: 홍길동
- 이렇게 해야겠지. 델리게이트도 마찬가지다.
startProcess += Digimon; 이렇게 +를 넣어줘야 함
private string ShowInfo(int num)
{
return ($"내 출석번호는 {num}번이야");
}
- 이제 이렇게 void 대신에 반환형과 매개변수가 있는 함수를 넣을 수 있는 델리게이트를 선언해 보자.
private delegate string StringDelegate(int a);
- 이렇게 하면 된다.
- string을 반환 받을 수 있는, int a를 매개변수로 받는 델리게이트 타입이라는 뜻
private delegate string StringDelegate(int a);
private event StringDelegate stringProcess;
private void Awake()
{
stringProcess = ShowInfo;
Debug.Log(stringProcess.Invoke(1));
Debug.Log(stringProcess(2));
}
private string ShowInfo(int num)
{
return ($"내 출석번호는 {num}번이야");
}
- 이러면
내 출석번호는 1번이야, 내 출석번호는 2번이야가 출력된다.
- 근데 만약 반환형이 있는 델리게이트에 함수가 여러 개 들어가 있으면 어떻게 될까?
- 그때는 마지막으로 추가한 메서드의 반환값이 return된다.
private delegate string StringDelegate(int a);
private event StringDelegate stringProcess;
private void Awake()
{
stringProcess = ShowInfo;
stringProcess += ShowSomething;
Debug.Log(stringProcess.Invoke(1));
}
private string ShowInfo(int num)
{
return ($"내 출석번호는 {num}번이야");
}
private string ShowSomething(int num)
{
return ($"맻기야! 네! {num}기 입니다!");
}
- 추가한 함수에는 += 처리를 해줘야 함
- 이러고 실행해 보면
맻기야! 네! 1기 입니다!가 출력된다.
- 마지막으로 추가된 함수의 반환값이 출력되게 되는 것.
- 근데 너무 귀찮지 않나? 싶어서 이걸 좀 더 간단하게 바꿀 수 있는 새 기능이 두 가지 생김
- 그게 바로 Action과 Func.
Action
Action / Func 차이?
- Action은 반환값이 없고(무조건 void) Func은 반환값이 있음(Something)
- Action은 void 함수를 받을 수 있는 델리게이트 타입, Func은 반환값이 있는 함수를 받을 수 있는 델리게이트 타입
private Action startAction;
private void Awake()
{
startAction = SayHI;
startAction.Invoke();
}
public void SayHI()
{
Debug.Log("안녕~");
}
- Action을 사용한 델리게이트 사용
- 이렇게 하면 안녕~ 이 실행됨
private Action startAction;
private void Awake()
{
startAction = SayHI;
startAction = Digimon;
startAction.Invoke();
}
public void SayHI()
{
Debug.Log("안녕~");
}
public void Digimon()
{
Debug.Log("디지몬~");
}
- 근데 이런 식으로 디지몬을 추가하면 델리게이트와 같이 디지몬~ 만 나오게 된다.
- 역시 두 개가 필요하면
startAction += Digimon; 이렇게 += 처리를 해줘야 함. (뺄거면 -=를 해주면 되겠지)
private Action<> startAction;
- Action의 <>에는 뭐가 들어갈까? Action은 반환형이 void라고 했으니 int 같은 건 아님
- 매개변수가 들어간다.
private Action<string> stringAction;
private void Awake()
{
startAction = SayHI;
startAction = Digimon;
startAction.Invoke();
stringAction += Pokemon;
stringAction("이상해씨");
}
public void Pokemon(string p)
{
Debug.Log($"가라 {p} 너로 정했다");
}
- 이렇게 string p를 매개변수로 받는 함수라면 <>안에 string을 넣어주면 되는 것
- 이 <>는 매개변수를 16개까지 받을 수 있다.
- 이러면
디지몬~, 가라 이상해씨 너로 정했다가 출력된다.
Func
- Func은 반환값이 있는 함수를 받을 수 있는 델리게이트 타입
private Func startFunc;
- 첫 Action처럼 간략하게 적으려 하면 여기서는 에러가 뜨게 됨
- 왜? 반환값이 없기 때문에. Func은 무조건 void가 아닌 반환형을 미리 지정해 줘야 함
private Func<string> startFunc;
private void Awake()
{
startFunc = ShowTV;
Debug.Log(startFunc.Invoke());
}
private string ShowTV()
{
return "난 TV를 켰어~";
}
- 이렇게
private string ShowTV()를 받으려면 <>에 string을 추가해 주면 된다.
- 실행해 보면 난 TV를 켰어~ 가 출력된다.
private Func<string> startFunc;
private void Awake()
{
startFunc = ShowTV;
Debug.Log(startFunc.Invoke());
}
private string ShowTV()
{
return "난 TV를 켰어~";
}
private string ShowProgram(int channelNum)
{
return $"나는 지금 {channelNum}번을 보고 있어";
}
- 근데 이렇게 string 반환값에 int 매개변수가 있는 걸 받으려면 어떻게 해야 할까?
private Func<string> startFunc; 여기서 추가로 int를 받아야 될 것 같다.
private Func<string, int> stringFunc;
private void Awake()
{
stringFunc = ShowProgram;
}
private string ShowProgram(int channelNum)
{
return $"나는 지금 {channelNum}번을 보고 있어";
}
- 근데 이렇게 하면
stringFunc = ShowProgram; 부분에 에러가 뜬다.
- 왜냐면 순서가 잘못됐음. 매개변수(int) 먼저 지정을 해주고 마지막에 반환형을 입력해야 한다.
private Func<int, string> stringFunc;
private void Awake()
{
stringFunc = ShowProgram;
Debug.Log(stringFunc.Invoke(7));
}
private string ShowProgram(int channelNum)
{
return $"나는 지금 {channelNum}번을 보고 있어";
}
- 이렇게 수정해 주고 실행해 보면
나는 지금 7번을 보고 있어가 출력된다.
event
- 델리게이트, Action, Func은 모두 이벤트를 사용하는 것이라고 말할 수 있다. (이벤트를 받을 수 있는 것)
private delegate string StringDelegate(int a);
private event StringDelegate stringProcess;
- 그래서 이렇게 event 키워드를 붙일 수 있다.
private event Action startAction;
private Action<string> stringAction;
private event Func<string> startFunc;
private Func<int, string> stringFunc;
- 이렇게 Action과 Func에도 event를 붙일 수 있다.
- event는 델리게이트의 특별한 형태로, 외부에서 직접 호출할 수 없고 오직 +=와 -= 연산을 통해서만 접근할 수 있도록 제한하는 기능을 제공함
- 이를 통해 다른 클래스에서 이벤트를 임의로 실행(Invoke())하지 못하도록 보호할 수 있다.
event를 사용하는 이유
- 델리게이트는 함수를 담을 수 있는 변수라고 볼 수 있는데, 델리게이트 변수 자체를 외부에서 변경할 수 있다는 단점이 있다.
public class EventTest
{
public delegate void MyDelegate();
public MyDelegate myEvent;
public void Start()
{
myEvent = SayHi;
myEvent?.Invoke();
}
public void SayHi()
{
Debug.Log("안녕~");
}
}
- 위 코드에서 myEvent는 public 필드이므로 외부 클래스에서 덮어쓰기(=)가 가능
- 즉 +=를 사용해야 하는데, 실수로 =를 사용하면 기존의 이벤트 핸들러가 모두 사라지게 됨
- 이 문제를 막기 위해 event를 사용하면 = 연산으로 덮어쓰는 것을 막을 수 있음.
public class EventTest
{
public delegate void MyDelegate();
public event MyDelegate myEvent;
public void Start()
{
myEvent += SayHi;
myEvent?.Invoke();
}
public void SayHi()
{
Debug.Log("안녕~");
}
}
- 그래서 이렇게 델리게이트 사용 시 event 키워드를 추가해 주면 외부에서 직접 호출할 수 없게 된다. (+=와 -=만 사용 가능)
UnityEvent
public UnityEvent uEv;
private UnityAction uAct;
- 이건 유니티에서만 쓸 수 있는 Event
- 이 두 개는 Action과 같이 반환값이 없는 함수들만 가질 수 있고 매개변수는 4개밖에 못 받음
- 그럼 왜 씀? 이라고 묻는다면 각각 특징과 장점이 있다.
- UnityEvent는 직렬화가 가능하다. 이 말은 인스펙터 창에서 확인이 가능하다는 뜻
- 유니티 인스펙터 창으로 가보면 uEv란이 생긴 걸 볼 수 있다. 버튼 설정 창과 비슷하게 사용할 수 있다.
- 그럼 UnityAction도 직렬화가 가능하나요? 아니요. 또한 그냥 Action보다도 성능이 안좋음
- 그럼 진짜 왜 씀? 정의하기로는 Unity에 연관되어 있어서 유니티 특화 기능을 사용할 때 좋다고 함. 근데 흠..
public UnityEvent uEv;
public void Start()
{
uEv.AddListner(SayHI);
uEv.Invoke();
}
public void SayHi()
{
Debug.Log("안녕~");
}
- UnityEvent는 이렇게 쓸 수 있다.
- 이건 사용할 때 Invoke();만 쓸 수 있음
private UnityAction uAct;
public void Start()
{
uAct += SayHI;
uAct.Invoke();
}
public void SayHi()
{
Debug.Log("안녕~");
}
- uAct은
uAct = SayHI;, uAct += SayHI; / uAct();, uAct.Invoke(); 둘 다 가능
public UnityEvent uEv;
private event UnityAction uAct;
- 그리고 event는 UnityAction만 가능. UnityEvent는 추가하면 에러가 뜬다.
정리
- 그럼 이것들을 다 어디다 쓰냐? 주로 콜백 함수에 쓴다.
- 콜백 함수는 예를 들어 코루틴 같은 것들을 쓸 때.
IEnumerator SomeProcess(Action<int> afterProcess)
{
Debug.Log("서버에 뭔가 보냄");
yield return new WaitForSeconds(2);
afterProcess.Invoke(1);
}
- 코루틴은 반환값이 없어서 반환을 할 수 없음
- 근데 해당 프로세스가 끝나고 무조건 뭔가가 진행됐음 좋겠다면 이렇게 해주면 됨