본문 바로가기

Programming

델리게이트 : 쉽고 재미있는 C# Programming 의 기본

 

1. 개념

 

'델리게이트 (Delegate ) : 대리자 (함수를 담는 상자)'

 

델리게이트는 다른 메서드를 참조하고 호출할 수 있는 형식으로, 실제로는 함수에 대한 참조를 보유하는 객체라고 생각할 수 있습니다.

  1. 대리자 (Delegate): 델리게이트는 다른 메서드에 대한 대리자 역할을 합니다. 즉, 델리게이트를 통해 메서드의 호출을 다른 객체에게 대리하거나 위임할 수 있습니다.
  2. 함수를 담는 상자: 델리게이트는 마치 함수를 담고 있는 상자와 같습니다. 델리게이트가 참조하는 메서드의 형식을 정의하고, 이 상자를 통해 메서드를 전달하거나 다른 곳에 저장할 수 있습니다.
  3. 콜백 함수: 델리게이트는 종종 콜백 함수를 구현하는 데 사용됩니다. 다른 메서드를 델리게이트로 전달하여 원하는 시점에 호출되도록 할 수 있습니다.
  4. 유연성과 다형성: 델리게이트는 타입 안전성과 다형성을 지원하므로, 여러 종류의 메서드를 동일한 델리게이트로 참조할 수 있습니다.

델리게이트 (Delegate) 는 메서드를 참조하는 형식으로, 메서드의 인터페이스를 정의하고 호출할 수 있는 객체입니다.

이를 통해 메서드를 매개변수로 전달하거나, 이벤트 처리와 콜백함수등의 시나리오에서 유연한 코드 작성이 가능합니다. 델리게이트(Delegate)는 메서드를 참조하는 형식으로, 메서드의 인터페이스를 정의하고 메서드를 호출할 수 있는 개체입니다. 델리게이트를 사용하면 메서드를 다른 메서드에 전달하거나 이벤트 처리와 같은 시나리오에서 유용하게 활용할 수 있습니다. 

 

델리게이트는 C#에서 함수 포인터로 볼 수 있으며, 타입 안정성과 다형성을 지원하여 코드의 재사용성과 확장성을 향상시킵니다. 콜백메서드, 이벤트처리, 람다표현식과 함께 사용될 때 강력한 기능을 발휘합니다.

 

2. 형식

delegate returnType DelegateName(parameters);

delegate 반환형식(데이터형) 델리게이트이름 (매개변수_인수목록); //형식

 

여기서 returnType은 델리게이트가 참조하는 메서드의 반환 형식이고, DelegateName은 델리게이트의 이름입니다. parameters는 델리게이트가 참조하는 메서드의 매개변수 목록을 나타냅니다.

 

예를 들어, 정수를 받아 정수를 반환하는 메서드를 참조하는 델리게이트는 다음과 같이 선언될 수 있습니다.

delegate int MyDelegate(int x);

Mydelegate delegateInstance = new Mydelegate(PlusCal);  //델리게이트 인스턴스 생성

 

3. 활용

 

델리게이트는 다양한 상황에서 활용됩니다:

  • 이벤트 처리: 델리게이트를 사용하여 이벤트를 처리하고, 이벤트가 발생했을 때 델리게이트가 참조하는 메서드가 호출됩니다.
  • 콜백 함수: 델리게이트를 사용하여 메서드를 다른 메서드의 인수로 전달하여 콜백 함수로 활용할 수 있습니다.
  • 비동기 프로그래밍: 델리게이트는 비동기 프로그래밍에서 콜백을 처리하거나 비동기 작업을 완료했을 때 호출될 메서드를 지정하는 데 사용됩니다.

4. 사용 예

 

4.1 간단한 델리게이트 활용

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

class Program
{
    // 델리게이트가 참조하는 메서드
    static void DisplayMessage(string message)
    {
        Console.WriteLine("Message: " + message);
    }

    static void Main()
    {
        // 델리게이트 인스턴스 생성
        MyDelegate delegateInstance = new MyDelegate(DisplayMessage);

        // 델리게이트를 사용하여 메서드 호출
        delegateInstance("Hello, delegates!");
    }
}

 

이 예제에서는 델리게이트를 사용하여 메서드를 참조하고 호출하는 간단한 경우를 보여줍니다. MyDelegate 델리게이트는 DisplayMessage 메서드와 같은 모양의 메서드를 참조할 수 있습니다. 델리게이트 인스턴스를 생성하고 해당 인스턴스를 사용하여 메서드를 호출할 수 있습니다.

 

4.2 이벤트 처리

// 이벤트를 처리할 델리게이트 선언
delegate void EventHandler(string message);

class Publisher
{
    // 이벤트 선언
    public event EventHandler MyEvent;

    // 이벤트 발생 메서드
    public void RaiseEvent(string message)
    {
        MyEvent?.Invoke(message);
    }
}

class Subscriber
{
    // 이벤트 처리 메서드
    static void HandleEvent(string message)
    {
        Console.WriteLine("Event handled: " + message);
    }

    static void Main()
    {
        Publisher publisher = new Publisher();
        Subscriber subscriber = new Subscriber();

        // 이벤트에 델리게이트 메서드 등록
        publisher.MyEvent += new EventHandler(HandleEvent);

        // 이벤트 발생
        publisher.RaiseEvent("Event message");
    }
}

 

이 코드는 C#에서 이벤트 처리를 구현하는 간단한 예제를 나타냅니다. 코드를 통해 이벤트 발생(Publisher)과 이벤트 처리(Subscriber)의 개념을 보여줍니다.

 

한 줄씩 살펴보면,

// 이벤트를 처리할 델리게이트 선언
delegate void EventHandler(string message);

 

여기서 EventHandler는 이벤트를 처리하는 델리게이트로, 이벤트가 발생할 때 호출될 메서드의 형식을 정의합니다. 이 경우, string 형식의 메시지를 받아들이는 메서드를 참조합니다.

class Publisher
{
    // 이벤트 선언
    public event EventHandler MyEvent;

    // 이벤트 발생 메서드
    public void RaiseEvent(string message)
    {
        MyEvent?.Invoke(message);
    }
}

 

MyEvent는 이벤트를 나타내며, 이 이벤트는 EventHandler 델리게이트를 사용합니다. 이벤트 선언은 클래스 내에서 특정 상황(이 경우, 이벤트가 발생할 때)에 다른 객체(Subscriber 등)에서 처리할 수 있는 메서드를 호출할 수 있도록 하는 메커니즘을 정의합니다.

 

RaiseEvent 메서드는 이벤트를 발생시키는 역할을 합니다. 이 메서드는 문자열 형식의 message를 인자로 받아서, 이벤트가 발생했을 때 등록된 델리게이트(이 경우, MyEvent에 등록된 메서드)를 호출합니다. MyEvent?.Invoke(message);에서 ?. 연산자는 MyEvent가 null이 아닌 경우에만 Invoke 메서드를 호출하도록 하는 것입니다.

class Subscriber
{
    // 이벤트 처리 메서드
    static void HandleEvent(string message)
    {
        Console.WriteLine("Event handled: " + message);
    }

    static void Main()
    {
        Publisher publisher = new Publisher();
        Subscriber subscriber = new Subscriber();

        // 이벤트에 델리게이트 메서드 등록
        publisher.MyEvent += new EventHandler(HandleEvent);

        // 이벤트 발생
        publisher.RaiseEvent("Event message");
    }
}

 

Subscriber 클래스의 HandleEvent 메서드를 이벤트에 등록합니다. 이 부분은 이벤트가 발생했을 때 실행될 메서드를 등록하는 부분입니다. 즉, MyEventHandleEvent 메서드를 델리게이트로 추가하라는 의미입니다.

이 메서드는 이벤트가 발생했을 때 호출되는 메서드로, 간단하게 콘솔에 이벤트 메시지를 출력합니다.

 

Main 메서드에서는 Publisher와 Subscriber 객체를 생성하고, MyEvent에 HandleEvent 메서드를 델리게이트로 등록합니다. 그 후, RaiseEvent 메서드를 호출하여 이벤트를 발생시킵니다. 이때, MyEvent에 등록된 델리게이트(여기서는 HandleEvent 메서드)가 호출되어 이벤트 메시지가 출력됩니다.

 

결과적으로 HandleEvent 메서드가 호출되어 "Event handled: Event message"가 출력됩니다.

 

델리게이트는 메서드처럼 사용하며 델리게이트에 데이터형이 다른 메서드를 대입하면 error 처리됩니다.

 

이벤트는 특별한 형태의 델리게이트로 생각할 수 있습니다. 다만, 일반적인 델리게이트와 달리 이벤트는 다른 클래스에서 이벤트에 직접 접근하여 델리게이트를 수정하는 것을 막기 위해 특별한 문법과 제한이 있습니다. 이렇게 함으로써 객체 간의 느슨한 결합을 유지하면서도 이벤트가 발생했을 때 특정 동작을 수행할 수 있도록 합니다.

 

[멀티델리게이트]

 

멀티델리게이트(Multicast Delegate)는 여러 개의 메서드를 동시에 호출할 수 있는 델리게이트입니다.

멀티델리게이트는 여러 델리게이트 인스턴스를 하나의 델리게이트로 결합하여 사용할 수 있게 해줍니다. 이것은 이벤트 처리와 같은 다양한 상황에서 유용하게 활용됩니다.

 

델리게이트는 데이터형이기때문에 델리게이트를 통해 생성된 객체 또한 연산이 가능합니다.

기존에 새로운 델리게이트를 추가할때는 += 연산자를 사용하고, 특정 델리게이트를 제거할  때는 -= 연산자를 사용합니다.

 

여러 개의 연결된 메서드를 호출하는데 각 메서드마다 반환값이 있다면 어떤 값을 우선적으로 변환해야 하는지 모호한 상황이 발생하기 때문에 멀티델리게이트의 반환형은 반드시 void 여야 합니다.

delegate void MultiDelegate(string message);

class Program
{
    static void Method1(string message)
    {
        Console.WriteLine("Method 1: " + message);
    }

    static void Method2(string message)
    {
        Console.WriteLine("Method 2: " + message);
    }

    static void Main()
    {
        // 멀티델리게이트 인스턴스 생성
        MultiDelegate multiDelegate = new MultiDelegate(Method1);

        // 다른 메서드 추가
        multiDelegate += new MultiDelegate(Method2);

        // 멀티델리게이트 호출 (모든 메서드가 호출됨)
        multiDelegate("Hello, multicast delegates!");

        // 메서드 2만 제거
        multiDelegate -= new MultiDelegate(Method2);

        // 멀티델리게이트 호출 (이제는 메서드 2가 호출되지 않음)
        multiDelegate("Goodbye, multicast delegates!");
    }
}

 

위의 예제에서 MultiDelegate는 두 개의 메서드를 참조하도록 설정되었습니다.

첫 번째는 Method1이고, 두 번째는 Method2입니다. += 연산자를 사용하여 두 번째 메서드를 추가하고, -= 연산자를 사용하여 두 번째 메서드를 제거했습니다. 따라서 멀티델리게이트를 호출할 때, 첫 번째 호출에서는 두 개의 메서드가 모두 호출되지만, 두 번째 호출에서는 두 번째 메서드가 호출되지 않습니다.


 

[요약]

 

  1. 델리게이트를 사용한 메서드 호출:
    • 델리게이트는 메서드를 변수처럼 저장하고 이를 실행할 수 있게 해줍니다.
    • 런타임(프로그램이 실행 중일 때)에 어떤 메서드를 호출할지 동적으로 결정할 수 있습니다.
    • 이는 코드를 더 유연하게 만들어주는데, 프로그램이 실행되는 동안 메서드 호출을 변경할 수 있습니다.
  2. 직접 메서드 호출:
    • 메서드를 직접 호출하는 경우에는 컴파일 시점(코드를 작성하고 컴파일할 때)에 어떤 메서드를 호출할지 결정됩니다.
    • 런타임에는 메서드를 동적으로 변경할 수 없습니다. 즉, 프로그램이 실행 중일 때 메서드 호출을 바꾸기 어렵습니다.
  3. 델리게이트 사용시의 예:
    • 예를 들어, 여러 이벤트 핸들러(메서드)가 있을 때, 이벤트가 발생할 때마다 어떤 핸들러를 실행할지 델리게이트를 사용하여 동적으로 결정할 수 있습니다.
    • 이것은 마치 여러 개의 함수를 가지고 있는 상자(델리게이트)를 만들어두고, 필요할 때마다 그 상자 안에서 특정 함수를 선택해서 실행하는 것과 비슷합니다.
  4. 직접 메서드 호출시의 예:
    • 반면에 직접 메서드를 호출하는 경우에는 특정한 메서드를 직접 호출하게 됩니다. 프로그램이 실행 중일 때 이 메서드 호출을 바꾸기 어렵습니다.

간단히 말하면, 델리게이트를 사용하면 프로그램이 실행 중일 때 메서드 호출을 동적으로 변경할 수 있어서 더 유연한 코드를 작성할 수 있습니다. 반면에 직접 메서드 호출은 컴파일 시점에 결정되며, 런타임에 동적으로 변경하기 어려운 코드를 의미합니다.

 

델리게이트를 사용한 동적 메서드 호출:

  • 델리게이트는 실행 중(런타임)에 메서드를 변경하고 호출할 수 있습니다. 이는 코드를 작성하는 시점에 미리 어떤 메서드를 호출할 것인지 결정하는 것이 아니라, 프로그램이 실행되는 동안에 결정할 수 있다는 의미입니다.
  • 예를 들어, 이벤트 처리에서 한 이벤트에 여러 메서드를 등록하고, 실행 중에 그 중 일부 또는 전체를 호출할 수 있습니다.

직접 메서드 호출:

  • 직접 메서드 호출은 컴파일 시점에 어떤 메서드를 호출할 것인지 결정되며, 프로그램이 실행 중일 때 변경할 수 없습니다.
  • 코드를 작성하고 컴파일할 때 어떤 메서드를 호출할 것인지 정확히 알아야 합니다.

간단히 말하면, 델리게이트를 사용하면 프로그램이 실행 중일 때 메서드 호출을 변경할 수 있지만, 직접 메서드 호출은 프로그램이 실행 중일 때 변경할 수 없습니다. 이러한 유연성은 특히 이벤트 처리나 콜백 함수 등에서 사용될 때 중요합니다.

 

예를 들어 사용자 입력을 기다리는 프로그램에서는 직접 메서드 호출을 사용할 수 있습니다. 그러나 입력을 받은 후에 그에 따라 동적으로 처리해야 할 때, 델리게이트를 사용하여 실행 중인 이벤트의 동작을 변경할 수 있습니다. 이를 통해 새로운 동작을 추가하거나 기존 동작을 변경할 수 있습니다.

델리게이트를 사용하는 이점 중 하나는 여러 이벤트 핸들러(메서드)를 동적으로 등록하거나 해제할 수 있다는 점입니다. 이것은 프로그램이 계속 실행되는 동안 유연하게 동작할 수 있도록 해줍니다. 만약 직접 메서드 호출을 사용한다면, 프로그램을 재시작하거나 중지해야 할 수도 있습니다.

따라서 델리게이트를 사용하면 실행 중인 이벤트의 동작을 중지시키지 않고도 동적으로 변경할 수 있어서 더 유연하게 프로그램을 구성할 수 있습니다.