[C#] Delegate

Lingtea_luv·2025년 3월 27일
1

C#

목록 보기
14/37
post-thumbnail

Deligate 대리자


우리가 여러 데이터를 각 형식에 맞게 자료형을 담은 것과 같이, 메서드를 담을 수 있는 자료형을 델리게이트라고 한다. 매개변수로 메서드의 동작 자체를 전달할 수 있기 때문에 내용은 짧으면서도 더 직관적인 코드 작성이 가능하다.

delegate

코드 영역에 존재하는 메서드의 주소를 가리키는 참조타입이기 때문에, 변수처럼 선언하여 사용하며 new 키워드로 인스턴스 생성이 가능하다.

public delegate float DeligateMethod1(float left, float right);
public delegate void DeligateMethod2(string str);
 
public static float Plus(float left, float right)
public static void Message(string message)
 
static void Main(string[] args)
{
    DeligateMethod1 mydel1 = Plus;
    DeligateMethod1 mydel2 = Message;  // 구문 오류
    DeligateMethod2 mydel3 = Message;
} 

delegate 선언과 함께 객체를 생성하고 함수를 추가할 때, 반드시 반환형과 매개변수가 일치해야한다. DeligateMethod1 로 생성한 mydel2에 매개변수가 다른 message 를 저장하려고 하면 구문 오류가 생기는 것을 확인할 수 있다.

Call Back

게임에서 버튼을 눌렀을 때 버튼의 종류에 따라 다른 기능을 수행하도록 만들어보자. 만약 delegate 기능을 몰랐다면 Button 클래스를 생성하고 이를 부모클래스로 하여 버튼의 종류마다 자식클래스를 만들어 상속하였을 것이다. 하지만 이렇게 할 경우 버튼의 종류가 많아질수록 클래스를 계속 생성해야하기에 프로그램에 부담이 될텐데, 이는 Delegate를 통해 해결할 수 있다.

public class Button
{
    public delegate void ClickListener();
    public ClickListener clicklistener;

    public virtual void Click()
    {
        if(clicklistener != null)
        {
            clicklistener();
        }
    }
}

public class File
{
    public void Save()
    {
        Console.WriteLine("저장합니다.");
    }

    public void Load()
    {
        Console.WriteLine("불러옵니다.");
    }
}

static void Main(string[] args)
{
    File file = new File();
    
    Button saveButton = new Button();
    saveButton.clicklistener = file.Save;
    
    Button loadButton = new Button();
    loadButton.clicklistener = file.Load;

    saveButton.Click(); // 저장하기
    loadButton.Click(); // 불러오기

방법은 Button 클래스에 delegate를 생성하여 여기에 File 클래스의 멤버함수를 추가하는 것이다. saveButtonButton 클래스로 생성되었기에 saveButton.clicklistenerfile.Save함수를 추가할 수 있는데, 이렇게 하면 saveButton.Click()를 실행했을 때 file.Save가 구동된다. 마찬가지로 loadButton.Click() 또한 file.Load 로 연결할 수 있으므로,Button 클래스 하나로 2개의 버튼을 만들 수 있게 된다.

여기서 saveButton.Click(); 호출을 통해 file.Save 함수가 구동되는 것을 볼 수 있는데 이렇게 직접적으로 호출되는 것이 아닌, 특정 조건에 반응하여 구동되는 함수를 콜백 함수라고 하며, Delegate를 활용하여 알람처럼 작동하는 것이 가능하다.

Generic delegate

협업을 하는 상황이라고 가정했을 때 만약 개발자 별로 독자적인 delegate를 선언하여 함수를 추가하게되면 delegate 이름만 보고 어떤 함수를 묶어놨는지 파악하기도 힘들뿐더러, 마찬가지로 매개변수 자료형과 반환형이 동일한 함수를 묶은건데 다른 delegate로 선언해서 호환이 불가능하게 되면 굉장히 불편할 것이다. 따라서 호완성을 높이기 위해 C#에는 자체적으로 반환형과 매개변수를 지정한 일반화 델리게이트가 존재한다.

  • Action
public delegate void MessageDelegate(string str);
public delegate void LogDelegate(string str);

static void Main(string[] args)
{  
    MessageDelegate stringDelegate = Message;
    LogDelegate logDelegate = Message;
         
    stringDelegate = logDelegate;  // 구문 오류

    Action<string> stringDelegate1 = Message;
    Action<string> logDelegate1 = Message;
}

Action<T> 는 반환형이 void인 델리게이트로, T에는 매개변수 자료형을 입력하면 된다. 위처럼 string 을 입력하면 매개변수의 자료형은 string이고 반환형이 void인 함수를 추가할 수 있는 것이다.

  • Func
int Plus(int left, int right) { return left + right; }
float Test(int value1, string value2) { return 0; }

Func<int, int, int> func1;
func1 = Plus;

Func<int, string, float> func2;
func2 = Test;

Func<T> 는 반환형과 매개변수의 자료형을 모두 입력받는 델리게이트이다. T에 자료형과 반환형을 입력하고, 반환형을 맨 마지막에 입력하는 특징을 가진다. 위처럼 <int, string, float> 을 입력하면 매개변수의 자료형은 intstring이고 반환형은 float인 함수를 추가할 수 있는 것이다.

Delegate Chain

static void Main(string[] args)
{
    int value = 10;
    value += 20;

    Action action;
    action = Func1;  // 델리게이트에 Func1 저장
    
    action += Func2; // 델리게이트에 Func2 추가 저장, 순차적으로 저장
    action += Func3;
    action += Func2;
    
    action -= Func1; // 델리게이트에 Func1 제거
    action -= Func2; // 델리게이트 제거시 나중에 추가 된거 제거(후입선출)
} 
Func2
Func3

위에서 action은 반환형이 void이고 매개변수가 없는 함수를 추가할 수 있는 델리게이트로 조건에 맞는 Func을 += 로 추가하거나 -= 로 제거할 수 있다. 이때 추가와 제거는 스택구조와 동일하게 순차적으로 이루어진다.

주의사항 : 대입연산자 = 을 쓰게되면 해당 함수로 초기화되기 때문에 대입연산자만 사용하는 것은 굉장히 위험할 수 있다.

profile
뚠뚠뚠뚠

0개의 댓글