[C#] 델리게이트(Delegate)와 Event로 함수를 깔끔하게 실행해보자!

2024. 11. 6. 18:41·C & C++ & C#

C#에서 델리게이트와 구독-알림 방식(Publish-Subscribe Pattern)을 사용하여 이벤트 기반 구조를 구현하는 방법을 자세히 알아봅시다잉. 이 방식은 특히 코드의 결합도를 낮추고, 여러 컴포넌트가 특정 이벤트에 반응할 수 있도록 할 때 유용합니다.


 

 

들어가기전 델리게이트가 왜 필요할까?

static void ButtonPressed() // 버튼이 눌렸을 때 실행
{
    // 버틀이 눌렸을 때 실행할 여러가지 함수들 나열 및 실행
}

 

버튼이  눌리면 아바타에 옷을 입히고~ 무기를 들게하고~ "전장에 나가자!" 라는 로직을 수행하게끔 하고싶다. 이런 과정을 순차적으로 실행되게 하고싶다면 버튼 눌리는 이 함수 내부에 각각의 기능들을 직접 실행시키면 된다는 것을 보편적으로 생각할 것이다. 다만 이런 방식의 문제점은 매개 변수에게 인수를 넘겨야 하기 때문에 개발자가 실수를 할 확률도 커지고, 게임 로직 함수 안에 UI 관련 함수가 섞여 있으니 설계적으로 보기 좋지 않은 등등 아쉽다. 특히 ButtonPressed() 같은 유니티에서 제공되는 이벤트 함수들은 함수 내부에다가 내가 원하는 함수를 직접 추가할 수도 없다,

 

 

1. 델리게이트(Delegate)란?

만약 위의 문제를 해결하기 위해 ButtonPressed()라는 함수이름처럼 버튼이 눌렸을 때 여러 함수들을 대신 실행해주면 얼마나 좋을까??? 실제로 이게 어떤 함수인지 몰라도 되며 그저 실행만 대신 해주는 함수를 델리게이드(Delegate 그래서 이름이 대리자) 라고 한다!

 

마치 실행 해야할 함수 포인터들만 ButtonPressed()에게 인자로 넘겨주고 "대신 실행 부탁드려용~~~" 하는 것과 같다.

그러면 일일이 실행될 함수들을 나열하여 직접 호출하고 그 함수들에게 집중해서 인자 넘겨할 필요도 없다.

델리게이트 선언과 사용 예시

 
// 델리게이트 선언
public delegate void Notify(string message);

class Program
{
    // 델리게이트 인스턴스 생성 후 사용하는 메서드
    static void SendMessage(string message)
    {
        Console.WriteLine("Message: " + message);
    }

    static void Main(string[] args)
    {
        // 델리게이트 인스턴스 생성
        Notify notifier = SendMessage;

        // 델리게이트 호출
        notifier("Hello, Delegates!");
    }
}

 

당연히 함수들을 대신 실행시켜주기 때문에 대신 시켜줄 함수들과 반환형, 매개 변수 타입과 수가 같아야한다!

 

그렇게 설정한 델리게이트가 위의 코드와 같이 함수를 대신 실행해준다.

 

 class Program
    {
        static void AscendingSort() { /* 오름차순 정렬시키는 내용*/ }
        static void DescendingSort() { /* 내림차순 정렬시키는 내용*/ }

        delegate void SortMethod();   

        static void Sort(SortMethod sortMethod)  
        {
            sortMethod(); 
        }

        static void Main(string[] args)
        {
            string input = Console.ReadLine();
            if (input == "1")
                Sort(AscendingSort);
            else
                Sort(DescendingSort);
        }
    }

 

예를 들어 정렬 함수를 구현한다고 할 때 이런식으로 오름 차순 정렬을 시키고 싶으면 Sort 함수에 AscendingSort 함수를 인수로 넘기면 되고, 내림 차순 정렬을 시키고 싶으면 Sort 함수에 DescendingSort 함수를 인수로 넘기면 된다. 정렬 방식에 따라 Sort 함수 내용이 달라지는건 없다. 어떤 함수들이 등록됐느냐에 따라 sortMethod();는 다르게 실행된다

 

 

 


다음은 두번째로 Event를 설명하기 전에 Event를 왜 사용할까에 대해 먼저 설명해보고자 한다.

        delegate int OnClicked();

        static void ButtonPressed(OnClicked clickedFunction)  
        {
            clickedFunction(); 
        }

        static void Main(string[] args)
        {
            OnClicked clicked = new OnClicked(Test);
            clicked += Test2; // chaining
            clicked();

            ButtonPressed(clicked); 

            clicked();
        }

 

 

위 코드를 보면 델리게이트의 한계에 대해서 알 수 있다.

델리게이트 형식 OnClicked를 오직 ButtonPressed() 함수 안에서만 실행시키기 위해 clickedFunction(); 만들었을 수도 있다. 그러나 상관 없는, 그리고 외부인 메인 함수 안에서도 clicked(); 이렇게 델리게이트를 실행시킬 수 있다는 문제가 있다.

 

2. 이벤트(Event)와 델리게이트

event는 delegate와 달리 이벤트와 같은 클래스 안에서만 이벤트 호출이 가능하다.

 

외부에선 오직 이벤트에 함수 등록만 해놓을 수 있으며 이벤트가 정의된 클래스 외부에서는 이벤트를 호출할 수는 없다. 이것이 델리게이트와의 차이점!

 

using System;
using System.Collections.Generic;
using System.Text;

namespace CSharp
{
    class InputManager
    {
        public delegate void OnInputKey();
        public event OnInputKey InputKey;  // public 접근 한정자까지 똑같이 맞춰주어야 함

        public void Update()
        {
            if (Console.KeyAvailable == false)
                return;

            ConsoleKeyInfo info = Console.ReadKey();
            if (info.Key == ConsoleKey.A)  // A 키가 눌리면 이벤트 호출
            {
                InputKey();  // 구독자들에게 메세지 뿌리기 👉 옵저버 패턴
            }
        }
    }
}

 

Console.KeyAvailable 👉 Key를 누를 수 있는 상태라면 True 리턴, Key를 사용할 수 없으면 False 리턴.
Console.ReadKey() 👉 사용자가 입력한 Key를 읽어들여 ConsoleKeyInfo 타입으로 리턴한다.

 

1️⃣ 먼저 delegate 형식부터 정의한다. (이벤트에 등록할 수 있는 함수 형식으로 쓸 것이다.)

2️⃣ 위에서 만든 delegate 형식을 가지는(= 타입이 해당 delegate인) 이벤트를 정의한다. 접근 한정자 + event + 델리게이트 이름 + 이벤트 이름

델리게이트와 그 델리게이트를 타입으로 하여 정의한 이벤트는 둘 다 접근 한정자가 동일해야 한다. 둘 다 public으로 동일하여 문제 없음.

사용자가 입력한 Key가 A라면 이벤트를 호출(실행)한다. 같은 클래스 내부에서 실행시키는 것이기 때문에 문제 없다.

📜InputManager 클래스 외부의 이곳 저곳에서 이 이벤트에 함수를 등록하고 이 이벤트에 등록된 함수가 무엇인지 알 필요 없이 그저 📜InputManager 클래스내에서 이를 실행만 하면 될 뿐이다.

이와 같이 구독자(등록된 함수들)들에게 메세지를 뿌리는 디자인 패턴을 옵저버 패턴이라고 한다

 


 

using System;
using System.Collections.Generic;

namespace CSharp
{
    class Program
    {
        static void OnInputTest()
        {
            Console.WriteLine("Input Received!");
        }

        static void Main(string[] args)
        {
            InputManager inputManager = new InputManager();
            inputManager.InputKey += OnInputTest;

            while (true)
            {
                inputManager.Update();
            }

            // inputManager.InputKey(); 컴파일 에러! 외부에서 호출 불가능
        }
    }
}

 

Program (외부. 메인 함수 있음) 에서 외부에서 실행되나를 봐보자.

이벤트를 사용하기 위해 📜InputManager 타입의 객체를 만들어 inputManager에서 이를 참조한다.

이벤트에 OnInputTest() 함수를 등록한다

inputManager.InputKey += OnInputTest;

이렇게 += 로 등록한다. 반대로 -=로 뺄 수 있다.

 

여기는 📜InputManager 클래스의 외부라서 이벤트를 직접 호출하는 것은 불가능하므로(inputManager.InputKey();는 컴파일 에러) 이 이벤트를 호출하는 📜InputManager의 멤버 함수 inputManager.Update();를 실행시키면 된다!

직접 이 안에서 입력이 들어오면 실행시킬 함수들을 구구절절 나열하고 직접 호출해줄 필요가 없이 inputManager.Update();만 실행시켜주면 땡이다!

 

결론! 델리게이트와 이벤트는 기능은 동일하나 차이점은 👉 델리게이트와 달리 이벤트는 클래스 외부에서는 이벤트를 호출할 수는 없다는 것이다.

'C & C++ & C#' 카테고리의 다른 글

[C++] C++로 문자열 연결하기 구현!  (0) 2025.01.31
[C#] 제네릭(Generic)의 제약조건에 대하여 정리  (0) 2024.11.11
[C#] 프로퍼티(property) 완벽 파헤치기!  (2) 2024.11.10
[C#] string.Formating() 다양한 데이터를 특정 형식으로 보기 좋게 만드는 함수!  (0) 2024.11.08
'C & C++ & C#' 카테고리의 다른 글
  • [C++] C++로 문자열 연결하기 구현!
  • [C#] 제네릭(Generic)의 제약조건에 대하여 정리
  • [C#] 프로퍼티(property) 완벽 파헤치기!
  • [C#] string.Formating() 다양한 데이터를 특정 형식으로 보기 좋게 만드는 함수!
개발준입니다
개발준입니다
백문이 불여일타
  • 개발준입니다
    Re:제로부터 시작하는 개발이야기
    개발준입니다
  • 전체
    오늘
    어제
    • All (32)
      • Java & Kotlin (3)
      • Spring (0)
      • DataBase (0)
      • Git & Github (1)
      • Baekjoon (8)
      • C & C++ & C# (5)
      • Unity (12)
      • DesignPattern (3)
      • SmallTalk (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    가중치 비교
    c#
    디자인패턴
    알고리즘
    코테
    자바
    매서드
    github
    티스토리챌린지
    리스너 패턴
    오블완
    개발자
    게임개발
    백준
    유니티
    알고리즘 #코테
    state pattern
    코딩
    레벨 디자인
    개발
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
개발준입니다
[C#] 델리게이트(Delegate)와 Event로 함수를 깔끔하게 실행해보자!
상단으로

티스토리툴바