2022.08.11 경일 메타버스 19주차 4일 수업내용. 대리자와 이벤트, 람다식, 추가 - 옵저버 패턴에의 활용
p. 472 ~ 475
이름이 없는 메소드
사용
대리자를 선언 후, 인스턴스 생성
delegate 키워드를 이용해 메소드의 구현이 담겨 있는 코드 블록을 참조
⇒ 여기서 코드 블록이 익명 메소드
대리자의 인스턴스를 호출하면 익명 메소드 호출
형식
대리자_인스턴스 = delegate ( 매개변수_목록 )
{
// 실행하고자 하는 코드
};
ex)
delegate int Calculate( int a, int b );
public static void Main()
{
Calculate Calc;
Calc = delegate ( int a, int b ) // 익명 메소드
{
return a + b;
};
Console.WriteLine( "3 + 4 : {0}", Calc( 3, 4 ) );
}
사용하는 이유 :
대리자가 참조할 메소드를 넘겨야 할 때, 이 메소드가 두 번 다시 사용할 일이 없다고 판단되면 익명 메소드를 활용
p. 476 ~ 479
객체에 일어난 사건 알리기
이벤트의 동작 원리는 대리자와 거의 비슷하다.
⇒ 이벤트는 대리자를 event 한정자로 수식해서 만든다.
사용 절차
대리자 선언
→ 대리자는 클래스의 안과 밖 어디에 위치해도 상관없다.
클래스 안에 대리자의 인스턴스를 event 한정자로 수식해서 선언
이벤트 핸들러 작성
→ 선언한 대리자와 일치하는 메소드
대상 객체의 이벤트에 이벤트 핸들러 등록
이벤트가 발생하면 이벤트 핸들러 호출
이벤트 활용 예시 코드 : p. 479
p. 480 ~ 481
대리자 : public 또는 internal로 수식되어 있으면 클래스 외부에서 호출 가능
이벤트 : public으로 수식되어 있어도 선언된 클래스 외부에서 호출이 불가능
이는 견고한 이벤트 기반 프로그래밍을 가능하게 한다.
포함된 객체만의 상태를 감시할 수 있다.
이러한 차이는 두 가지의 용도를 분리한다.
대리자 ⇒ 콜백 용도
이벤트 ⇒ 객체의 상태 변화 / 사건의 발생을 알리는 용도
자료 : 교과서 “이것이 C#이다” ch.14 람다식 p. 485
1936년 수학자 알론조 처치(Alonzo Church)가 발표한 람다 계산법에서 사용하는 식
람다 계산법은 함수의 정의와 변수, 함수의 적용으로 구성
모든 것이 함수로 이루어져있다.
주류 프로그래밍 언어는 대부분 람다식을 지원한다.
익명 메소드를 만들기 위해 사용
형식
매개변수_목록 => 식
ex)
delegate int Calculate(int a, int b);
//.
..
static void Main(string[] args)
{
Calculate calc = (int a, int b) => a + b;
}
C# 컴파일러는 “형식 유추(Type Inference)” 기능을 제공하여 코드를 간결히 만든다.
delegate int Calculate(int a, int b);
//.
..
static void Main(string[] args)
{
Calculate calc = (a, b) => a + b;
}
p. 490 ~ 491
람다식의 오른편에 식 대신 코드 블록이 온다.
(매개변수_목록) => {
문장1;
문장2;
문장3;
}
ex)
delegate void DoSomething();
//.
..
static void Main(string[] args)
{
DoSomething DoIt = () =>
{
Console.WriteLine("뭔가를");
Console.WriteLine("출력해보자");
Console.WriteLine("이렇게");
};
DoIt();
}
문 형식의 람다식을 활용한 예시 코드 ; p. 491 ~ 492
p. 492 ~ 495
별도로 대리자를 선언하지 않아도 사용할 수 있는,
미리 선언된 대리자
Func 대리자는 결과를 반환하는 메소드를 참조
Action 대리자는 결과를 반환하지 않는 메소드를 참조
결과를 반환하는 메소드를 참조
17가지 버전 → 1개의 형식 매개변수 ~ 17개의 형식 매개변수
가장 마지막 형식 매개변수 ⇒ 반환 형식
다음의 예외가 아니면 별도의 대리자를 선언할 필요가 없다.
Func 대리자와 흡사하다.
결과를 반환하지 않는 메소드를 참조
⇒ 반환 형식 없음
17가지 버전 → 0개 ~ 16개 형식 매개변수
⇒ 형식 매개변수가 모두 입력 매개변수
일련의 작업 수행이 목적
https://docs.google.com/document/d/18eUQPpJqRbfC9v1XYP5b9u4H7PDUeIPcW4aOrxD11NE/edit#
delegate
private delegate void SecondElapsedDelegate();
// void() 타입의 함수만 보관할 수 있다.
// delegate는 타입을 정의한다.
위와 같은 코드 :
public class SecondElapsedDelegate : System.MulticastDelegate
{
}
// 이처럼 System.MulticastDelegate를 상속받는 것을
// delegate라는 한정자로 축약한 것
호출 방법
OnSecondElapsed(); 로 연결된 메소드를 모두 호출
OnSecondElapsed.Invoke()로 호출(통지)
팁
대리자 체인에 메소드를 연결할 때, 중복을 방지하기 위해
항상 먼저 해당 함수의 연결을 끊고 다시 연결하자.
코루틴은 외부에 노출시키면 StartCoroutine으로 외부에서 실행해야한다.
그렇기에 코루틴은 내부로 숨기고 다른 public 함수에 StartCoroutine을 넣어 넘겨주는 편이 좋다.
함수를 보관할 수 있는 타입
사용자 정의 대리자를 만들 수도 있지만 System.Action 혹은 System.Func 혹은 Unity에서는 UnityAction을 사용할 수 있다.
Invoke() 메소드로 보관된 모든 함수를 호출할 수 있다.
옵저버 패턴을 쉽게 사용하기 위한 타입
선언된 대리자 타입의 함수를 보관할 수 있다.
대리자와 다르게 외부에서 Invoke()를 할 수 없다.
함수가 중복되어 추가 된다면 디버깅이 매우 어렵기 때문에, 꼭 추가 전에는 삭제를 해준다.
이벤트 객체에 아무런 함수도 없는 경우에는 null이기 때문에 Invoke()를 호출할 때는 꼭 Null 체크를 해준다.
활용 방법은 "Event"의 의미대로 특정 사건(혹은 시점)을 나타내며, 그 시점에 일어나야 하는 일들(코드 상에서는 메소드로 표현)을 일으킬 수 있다.
주의 사항 / 하기 쉬운 실수
특정 시점이 됐는데 Invoke() 호출을 빼먹은 경우
Invoke() 될 때 실행되어야 할 메소드를 추가하지 않은 경우
객체 지향 설계의 핵심은 메시지다.
메시지 전달을 코드로 표현하려면 메소드를 호출하면 된다.