유니티/C#

[C#] Delegate(대리자) : Event, Lambda

HungryK 2025. 3. 5. 00:44

 

공부용으로 작성되는 페이지입니다. 틀린 부분이나 환경에 따라 오류가 발생할 수 있습니다. 



 


 

1. Delegate 란?

'대리자' 라는 의미를 가지고 있으며 함수의 주소값을 가지고 대신 호출한다. Class, Array, Interface와 같은 참조형 타입이다.

메서드를 매개변수로 전달하거나, 런타임에 호출될 메서드를 동적으로 연결할 때 유용하며 매개변수로 가져오는 덕분에 결합도를 낮출 수 있어 C언어의 포인터와 유사하다. 

 

델리게이트는 다른 함수를 특정 함수값으로 반환하고싶거나 매개변수로 가져오고 싶을때, 혹은 Call back이 필요할 때 

사용한다. 그리고 시그니처만 동일하다면 여러개의 함수를 참조할 수 있다.

 

2. Delegate 사용하기: 정의와 선언 

 

Delegate를 선언할 때는 호출할 함수의 시그니처(형식)을 확인하도록한다. 

#델리게이트선언 방법

delegate 반환형식 델리게이트명(매개변수..); 
#example
delegate int TestInt(int x, int y);
delegate void TestDelegate();
#필요에 따라 접근제한자를 함께 작성한다

 

#델리게이트 선언과 사용법


	#ex1
    
    public delegate void TestDelegate(); #델리게이트 정의 

    public TestDelegate testDelegate; #델리게이트 객체(변수) 선언 

    private void Start() 
    {
        testDelegate = LogPrint;

        testDelegate();
    }

    private void LogPrint() #참조 메소드 
    {

        Debug.Log("Testwrite"); #테스트용 Log 출력 함수 

    }
    
    
    #ex2
    
    
    public delegate int MathDelegate(int x, int y); //델리게이트 정의 

    MathDelegate mathDelegate = new MathDelegate(divid); //델리게이트 객체 선언


        private void Start()
    {
        int res = mathDelegate(120, 2);
        Debug.Log(res); 
    }

    static int divid(int x, int y) { return x / y; }

 

 

 

 

3. Delegate Chain : 여러 함수 참조하기 

1에서 언급했듯이 Delegate는 여러 함수를 동시에 참조할 수 있다.  이것을 Delegate Chain이라고 한다.

주로 +=를 사용해서 참조를 추가한다. 반대로 -=를 사용해서 참조를 해제할 수 있다. 

 

    
  #예제
  #레벨업 
    
  int _Mylevel = 4; #현 레벨  
  int _MyHP = 30; #fullHP는 100
  int _Myexp = 90; #레벨업하면 exp는 0으로 초기화된다. 


 #delegate선언 및 객체
public delegate void LevelupDelegate(int level, int HP, int exp);
LevelupDelegate levelupDelegate;

    void Start()
    {
        #delegate chain, 여러 함수를 참조한다 
        levelupDelegate = LVup;
        levelupDelegate += HPfull;
        levelupDelegate += expback;

        levelupDelegate.Invoke(1, 100, 0);
 
    }



#참조할 함수들 
    void LVup(int level, int HP, int exp)
    {
        _Mylevel = _Mylevel + level;
        Debug.Log("Level : " + _Mylevel);
    }

    void HPfull(int level, int HP, int exp)
    {
        _MyHP = HP;
        Debug.Log("HP : " + _MyHP);

    }

    void expback(int level, int HP, int exp) 
    {
        _Myexp = 0;
        Debug.Log("exp : " + _Myexp);

    }

 

* 사실 이 예시의 상황(레벨업)은 하나의 함수로 충분히 가능하기때문에 델리게이트 체인을 써야할 상황은 아니다. 어디까지나 이런식으로 여러 함수를 동시에 참조할 수 있다는 것에 주목하자. 

 

4. Event와 EventHandler 

  • Event

Event는 조건을 만족 했을 때 객체의 상태 변화나 사건이 자동으로 실행되도록 하는 기능이다.

event 키워드와 함께 쓰이며, 외부에서 호출할 수 없다. 이런 특성상 안정적인 프로그래밍이 가능하다.

이벤트 여러 구독자들이 있을 때 사용하는 것이 좋다.

 

즉, 한 사건이 발생했을 때 여러 구독자들이 그 이벤트에 영향을 받아야 할 때 쓰는 것이다. 

 

#ex
#모든 적들에게 플레이어의 위치를 전달하는 이벤트

 

sphere가 Player, 나머지는 모두 Enemy라고 가정한다

 

#1. 이벤트를 관리하는 이벤트 매니저 - delegate event를 작성 

public delegate void PlayerFindEvent(Vector3 playerposition);
public static event PlayerFindEvent OnPlayerFind;

public static void KnowAllEnemy(Vector3 playerposition)
    {
        OnPlayerFind.Invoke(playerposition);
    }

 


#2. 구독자(Enemy)

  private bool FindPlayer = false;
  private Vector3 lastknownPlayerPosition;
  
  void Start()
  {
      LinkInfo(); #보통 생성되거나 소멸할 때 이벤트 구독과 해지를 해주는게 좋다. 


  private void Update()
  {
      if(!FindPlayer)
      {
          DetectPlayer();
      }
      else
      {
          ChasePlayer();
      }
  }

  private void DetectPlayer()
  {
     
      Collider[] hitColliders = Physics.OverlapSphere(transform.position, 5f); // 5m 범위 탐색

      foreach (Collider col in hitColliders)
      {
          if (col.CompareTag("Player")) #플레이어가 범위 안에 있으면
              {
              Debug.Log(gameObject.name + "이(가) 플레이어를 발견했다!");
              FindPlayer = true;
              lastknownPlayerPosition = col.transform.position;

              #모든 적에게 플레이어 발견 알림
              EventManger.FindPlayerEvent(lastknownPlayerPosition);
              break;
          }
          else { Debug.Log("순찰중입니다..."); }
          }
      }

  private void LinkInfo() #이벤트 구독용 메소드 
  {
      Debug.Log("아군과 정보를 링크합니다.");
      EventManger.OnPlayerFind += OnPlayerDetected; //시그니처가 동일하므로 체인해줄 수 있다.
  }

  private void OnPlayerDetected(Vector3 playerPosition)
  {
      if(!FindPlayer) #플레이어를 찾지 못 한 Enemy들의 경우에만
      {
          Debug.Log("정보를 제공받았습니다");
          FindPlayer = true;
          lastknownPlayerPosition = playerPosition;
      }

  }

   private void ChasePlayer() #이 부분은 따로 작성하지 않았다. 
  {
      if(FindPlayer == true)
      
      {
          Debug.Log("추격내용");
      }
     
  }

 

테스트용으로 하나의 Enemy만 Player를 인식할 수 있는 거리에 배치하였다

 

잘 작동되는 것을 확인할 수 있다

 

 

 

  • EventHandler

EventHandler는 Unity system내에 이미 델리게이트로 정의가 되어 있기 때문에 따로 델리게이트를 선언하지 않고 그대로 사용한다.

#EventHandler의 구성 
public delegate void EventhHandler(object sender, EventArgs e);

 

object Sender : 이벤트를 실행시키는 객체

EventArgs : 이벤트 실행 시 전달하고 싶은 정보에 대한 매개변수. 상속으로 사용한다. 

 

public event EventHandler eventhandler; #using System;이 작성되어있다면, 이렇게 바로 사용할 수있다.

 

 

* 이벤트 핸들러의 경우 예시 코드를 작성할만큼 이해하지 못 했으므로 다시 공부 후 작성한다. 

 

5. Lambda 

 

Lambda식은 즉각적으로 사용되어야하거나 이름이 없는 함수를 편리하게 사용할 수 있게 해주는 익명 함수이다.

즉 별도의 메소드 선언 없이 간결하게 작성할 수 있다. 다만 익명 함수인만큼 디버깅이 어려우므로 주의해서 사용해야한다. 

 

 

Delegate = (a, b) => a + b;

 

람다식은  =>으로 표현된다.

 

#LambdaExample

delegate int TestDelegate(int a,int b);

 private void Start()
 {
     TestDelegate testDelegate;
     testDelegate = (int a, int b) => a+ b; //람다식

     int res = testDelegate(3, 5);
     Debug.Log(res); 
 }

 

 

 


 

 

참고 자료 

 

 

 

C# event 사용(delegate)

1. event 개념 이벤트(event)의 용어 정의는 "사건"을 의미합니다. 프로그래밍에서 event도 비슷한 개념으로 "어떠한 조건을 만족하는 사건"이 발생했을 때, 자동으로 실행을 할 수 있도록 하는 것 입

woojoolog.tistory.com

 

 

 

C# 프로그래밍의 기초 : Lambda (람다식)

람다식은 C#에서 강력하고 유연한 프로그래밍을 가능하게 하는 중요한 기능 중 하나입니다. 이는 익명 함수를 간결하게 표현할 수 있는 방법을 제공하며, 매개변수 목록, => 연산자, 그리고 식 또

unialgames.tistory.com

 

 

c# delegate, event 사용법 및 사용예제

1. delegate란 delegate는 메서드 대리자입니다. delegate로 메서드 대리자를 선언해주고 원하는 메서드를 참조시킬수 있습니다. 쉽게말하면 함수를 보관하는 통을 만들고(대리자선언) 그 통안에 함수

frozenpond.tistory.com