전체 코드

using System.ComponentModel;
using System.Numerics;
using System.Threading;
using System.Collections.Generic;

namespace CSharp
{

    class Program
    {

        delegate int OnClicked(); // 함수가 아니라 형식 -> 자체가 하나의 형식임
        // delegate가 붙으면 -> 형식은 형식인데, 함수 자체를 인자로 넘겨주는 형식
        // 반환은 int 입력 : void
        // Onclicked delegate 형식의 이름이다.


        // UI
        static void ButtonPressed(/*함수 자체를 인자로 넘겨주고*/OnClicked clickedFunction)
        {
            //
            //
            //
            // 여기 다 작성하면
            // UI 코드와 게임 로직 코드가 꼬일 경우가 생김
            // 현실적으로 안에 있는 코드를 수정하기 힘들 경우가 생김

            // 함수를 호출
            clickedFunction();

        }

        static int TestDelegate()
        {
            Console.WriteLine("Hello delegate");
            return 0;
        }
        static int TestDelegate2()
        {
            Console.WriteLine("Hello delegate2");
            return 0;
        }
        static void Main(string args)
        {
            // delegate 대리자
            // 순차적으로 코드를 작성하고 실행했지만
            // 그러지 않을 경우도 생김
            // ex -> 사장님 비서 - 우리의 연락처 // 용건 거꾸로 -> 연락을 주세요

            // ex -> UI
            ButtonPressed(TestDelegate);


            // 다양한 동작을 하는 하나의 함수를 만들때 유용함
            // ex sorting 함수

            // Delegate chaining
            OnClicked clicked = new OnClicked(TestDelegate);

            clicked += TestDelegate2;


        }
    }
}

✅ Delegate란?

Delegate함수를 저장하고 호출할 수 있는 특수한 객체입니다.

  • C++의 함수 포인터와 비슷하지만 더 강력합니다.
  • 여러 함수 등록(체이닝) 가능
  • 런타임에 함수 교체 가능
  • 콜백 함수 구현에 최적화

🔎 왜 Delegate를 사용할까?

상황문제점
특정 이벤트가 발생했을 때 실행할 함수가 정해져 있음이벤트마다 함수를 하드코딩해야 함
버튼 클릭 시 다양한 동작 수행UI와 로직이 강하게 결합
함수 변경 시마다 이벤트 코드를 수정해야 함유지보수 어려움

해결책: Delegate 사용

  • 이벤트 발생 시, 무슨 함수인지 모르고 대신 실행만 하면 됨
  • 실행할 함수를 외부에서 등록 가능
  • 코드 결합도↓, 유연성↑, 유지보수 편해짐

📐 Delegate 기본 구조

delegate int OnClicked();
  • OnClicked라는 대리자 타입 정의
  • 매개변수 없음 (void)
  • 반환형 int
  • 이 조건을 만족하는 메서드만 등록 가능

💻 Delegate 기본 예제

class Program
{
    delegate int OnClicked();  // 대리자 선언

    static int Test()  // 대리자가 참조할 메서드
    {
        Console.WriteLine("Hello Delegate");
        return 0;
    }

    static void Main()
    {
        OnClicked clicked = new OnClicked(Test);  // 대리자 객체 생성 및 메서드 연결
        clicked();  // 대리자 실행 = Test() 실행
    }
}

흐름 정리

단계설명
1OnClicked라는 대리자 타입 정의
2Test() 메서드 작성
3clicked라는 대리자 객체 생성 & Test 연결
4clicked() 실행 = Test() 실행

🔗 Delegate 체이닝 (Chaining)

clicked += Test2;  // 추가 등록
clicked();         // 등록된 모든 함수 순차 실행
  • += 로 여러 메서드 연결 가능
  • 호출 시, 등록 순서대로 모두 실행
  • -= 로 특정 메서드 삭제 가능

📥 Delegate를 매개변수로 받는 함수

static void ButtonPressed(OnClicked clickedFunction)
{
    clickedFunction();  // 대리자 실행 = 등록된 함수 실행
}
  • 함수의 매개변수로 대리자 전달 가능
  • 이러면 버튼 클릭 시 실행 함수 변경도 매우 쉬워짐

📝 함수 포인터 vs Delegate 비교

기능함수 포인터(C++)Delegate(C#)
타입 안전성약함강함
메서드 체이닝불가능가능
이벤트 연계어렵다매우 쉬움
다형성 지원약함강함
콜백 구현가능매우 편리

💡 Delegate의 장점

✅ 메서드 교체, 변경이 쉬움
✅ 이벤트 처리 유연
✅ 코드 결합도↓ (UI와 로직 분리)
✅ 체이닝으로 여러 동작 한번에 실행 가능
✅ 콜백 함수 구현 편리


⚠️ 주의점

🚨 등록된 함수가 없을 때 호출하면 NullReferenceException 발생 가능

OnClicked clicked = null;
clicked();  // 예외 발생!

✅ 호출 전 null 체크 필수

clicked?.Invoke();

⚡ 실제 사용 예 - 정렬 함수 선택

class Program
{
    delegate void SortMethod();

    static void AscendingSort() { Console.WriteLine("오름차순 정렬"); }
    static void DescendingSort() { Console.WriteLine("내림차순 정렬"); }

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

    static void Main()
    {
        string input = Console.ReadLine();
        if (input == "1")
            Sort(AscendingSort);   // 오름차순 선택
        else
            Sort(DescendingSort);   // 내림차순 선택
    }
}
  • 정렬 방식(오름/내림)을 선택할 때,
  • 실제 정렬 함수 자체를 넘겨서 호출하는 구조
  • 정렬 방식이 늘어나도 Sort 함수 수정 없음 → 확장성 높음

🔥 Delegate + Generic (일반화)

delegate T SortMethod<T>(T[] array);

static void Sort<T>(T[] array, SortMethod<T> sortMethod)
{
    sortMethod(array);
}
  • 타입에 관계없이 정렬 가능
  • 강력한 확장성 확보

profile
李家네_공부방

0개의 댓글