비트를 쪼개는 개발자

allen321@naver.com

C#

C# - 이벤트(Event) / 델리게이트와 이벤트의 차이점

MozarTnT 2024. 8. 21. 15:14
728x90
반응형

 

 

 

 

 

이벤트(Event)란?

 

 

  • 이벤트는 객체 또는 클래스에서 특정 상황이 발생하거나 상황을 발생시켰을때 알림을 받기 위해 사용되는 메커니즘이다.

 

  • 특정한 조건이 충족되면 이벤트가 호출되며 버튼 클릭, 마우스 이동이나 키보드 입력 등 다양한 작업을 수행하다가 발생하는 이벤트를 처리하기 위해 사용된다.

 

  • 이벤트는 기본적으로 델리게이트를 기반으로 작동하며 이벤트를 정의할 때에는 해당 이벤트를 처리할 델리게이트 형식을 정의해야 한다.

 

  • 이벤트는 상호 참조를 막기 위해 대입 연산자를 사용할 수 없고, 증감 연산자만 사용할 수 있다.(+=, -=)

 

  • 이벤트는 객체 간 느슨한 결합을 유지하면서 상호 작용을 유지하며 하나의 이벤트에 여러 응답을 지정해 확장성이 뛰어나다.

 

이벤트의 사용법은?

 

 

우선 이벤트를 사용하려면 두 역할을 수행할 클래스 및 객체가 필요하다.

 

 

1. 이벤트 발생자 (Event Provider)

  • 이벤트 발생자는 말 그대로 이벤트를 발생시키는 역할을 수행하는 객체 및 클래스이다. 
  • 이벤트 발생자 내에서 이벤트를 정의해야 하며 이벤트가 발생할 때는 이벤트 발생자를 호출해야 한다.
  • ex) 버튼 클릭 이나 마우스 이동과 같은 이벤트를 발생시키는 버튼 객체 및 마우스 객체이다.

 

 

2. 이벤트 구독자 (Event Subscriber)

  • 이벤트 구독자는 이벤트를 감지하고 해당 이벤트에 대해 반응하는 메서드를 가진 객체 및 클래스이다.
  • 이벤트 발생자에서 발생한 이벤트를 감지하며 그에 대한 처리를 모두 담당한다.
  • ex) 버튼 클릭 이후 이를 처리하는 역할을 하는 코드를 가지며 이러한 처리 코드를 가진 객체가 이벤트 구독자 역할을 수행한다.

 

 

 

이벤트의 구성 요소 및 흐름

 

 

1. 델리게이트 정의 (이벤트 핸들러의 형식 정의)

  • 특정 이벤트가 일어났을때 작업을 수행할 메서드를 정의한다.
  • 여러개의 이벤트 핸들러가 하나의 이벤트에 연결이 가능하다.
  • 델리게이트 선언 시 이벤트가 발생 되었을때 호출할 메서드의 반환 타입과 매개변수를 정의해야 한다.
public delegate void Notify();  // 예시) 반환 값이 없고 매개변수가 없는 메서드를 참조하는 델리게이트

 

 

2. 이벤트 정의

  • 델리게이트를 사용해 이벤트를 정의(선언)한다.
  • 위 이벤트는 특정 상황이 발생하면 이를 이벤트 구독자에게 알리는 역할을 수행한다.
  • 클래스 내에 선언된 이벤트는 델리게이트를 기반으로 선언되어야 한다.
public event Notify ProcessCompleted;
// ProcessCompleted 이벤트는 Notify 델리게이트를 기반으로 정의해야 하며
// 특정 프로세스가 완료되었을 때 발생할 수 있는 이벤트로 설정한 예시이다.

 

 

3. 이벤트 발생

  • 특정 작업이 완료되거나 특정 조건이 충족되면 이벤트가 발생한다.
  • 이벤트가 발생하면 해당 이벤트를 구독한 모든 이벤트 핸들러(Event Handler)가 호출된다.
  • 이벤트 발생 작업은 일반적으로 이벤트를 발생시킨 클래스 내부에서 수행된다.
protected virtual void OnProcessCompleted()
{
    ProcessCompleted?.Invoke();  // 이벤트 발생
}
// 이 코드는 OnProcessCompleted 메서드에서 ProcessCompleted 이벤트를 발생시키는 역할을 수행한다.
// ?.Invoke()는 이벤트에 등록된 핸들러가 있는 경우에만 이벤트를 발생시킨다.

 

 

4. 이벤트 핸들러(Event Handler) 정의

  • 이벤트가 발생되면 실행할 메서드를 정의한다.
  • 이벤트 핸들러는 이벤트 발생시 수행할 동작들을 이벤트 핸들러 내에 반드시 가지고 있어야 한다.
private static void Bl_ProcessCompleted()
{
    Console.WriteLine("Process Completed!");
}
// 이 메서드는 ProcessCompleted 이벤트가 발생했을 때 호출될 이벤트 핸들러로
// "Process Completed!"라는 메시지를 출력하는 동작을 수행한다.

 

 

 

5. 이벤트 구독 (Event Subsciption)

  • 이벤트 핸들러를 이벤트에 구독시켜 이벤트가 발생되면 해당 이벤트 핸들러를 호출하도록 설정한다.
  • 이벤트 구독은 증감 연산자를 사용하며 (+=) 하나의 이벤트에 여러 이벤트 핸들러를 구독할 수도 있다.
bl.ProcessCompleted += Bl_ProcessCompleted;
// ProcessCompleted 이벤트에 Bl_ProcessCompleted 핸들러를 등록하며
// 이벤트가 발생할 때 Bl_ProcessCompleted 메서드가 호출되도록 설정한다.

 

 

6. 이벤트 구독 취소 (Event Unsubscription)

  • 이벤트 핸들러를 이벤트에 구독 시킬수도 있지만 더 이상 해당 이벤트에 반응하고 싶지 않다면 이벤트 핸들러를 이벤트에서 제거할 수도 있다.
  • 이벤트 구독 취소 역시 증감 연산자를 사용하며 (-=) 취소된 이벤트 핸들러는 더 이상 해당 이벤트에 반응하지 않는다.
bl.ProcessCompleted -= Bl_ProcessCompleted;
// ProcessCompleted 이벤트에서 Bl_ProcessCompleted 핸들러를 제거한다.
// 이후 이벤트가 발생하더라도 이 핸들러가 호출되지 않는다.

 

 

 

종합 코드 예시

 

using System;

public delegate void Notify();  // 1. 델리게이트 정의

public class ProcessBusinessLogic
{
    public event Notify ProcessCompleted;  // 2. 이벤트 정의

    public void StartProcess()
    {
        Console.WriteLine("Process Started!");
        // 작업 수행 후 이벤트 발생
        OnProcessCompleted();
    }

    protected virtual void OnProcessCompleted()
    {
        // 3. 이벤트 발생
        ProcessCompleted?.Invoke();
    }
}

public class Program
{
    static void Main(string[] args)
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();

        // 5. 이벤트 구독
        bl.ProcessCompleted += Bl_ProcessCompleted;

        bl.StartProcess();

        // 6. 이벤트 구독 해지
        bl.ProcessCompleted -= Bl_ProcessCompleted;
    }

    // 4. 이벤트 핸들러 정의
    private static void Bl_ProcessCompleted()
    {
        Console.WriteLine("Process Completed!");
    }
}

 

 

 

위 코드를 간단하게 정리하면 다음과 같은 기능을 수행한다.

  • 델리게이트 정의: Notify 델리게이트가 이벤트 핸들러의 형식을 정의함.
  • 이벤트 정의: ProcessCompleted 이벤트가 Notify 델리게이트 형식으로 선언됨.
  • 이벤트 발생: OnProcessCompleted() 메서드가 호출될 때 ProcessCompleted 이벤트가 발생함.
  • 이벤트 핸들러 정의: Bl_ProcessCompleted 메서드는 이벤트 발생 시 호출될 동작을 정의함.
  • 이벤트 구독: ProcessCompleted 이벤트에 Bl_ProcessCompleted 핸들러를 등록하여 이벤트가 발생할 때 이 핸들러가 호출되도록 함.
  • 이벤트 구독 해지: 더 이상 이벤트에 반응하고 싶지 않을 때, 핸들러를 이벤트에서 제거한다.

 

 

이벤트와 델리게이트의 차이점

 

 

  • 직접 호출 가능 여부:
    • 델리게이트는 델리게이트 인스턴스를 통해서 직접 메서드를 호출할 수 있다.

    • 이벤트는 이벤트를 소유한 클래스 외부에서 직접 호출할 수 없다.
    • 이벤트 제공자만이 이벤트를 발생시킬 수 있으며, 이벤트에 연결된 메서드는 이벤트가 발생할 때만 호출된다.
  • 접근 제한:
    • 델리게이트는 외부에서 직접 접근하고 호출할 수 있다.

    • 이벤트는 이벤트 제공자 클래스 외부에서 직접 호출하거나 설정할 수 없도록 보호된다.
    • 이벤트 클래스 외부에서는 오직 이벤트에 핸들러를 등록하거나 제거할 수만 있다.
  • 사용 목적:
    • 델리게이트는 함수 포인터로서 다양한 목적(예: 콜백 메서드 전달, 동적 메서드 호출 등)으로 사용한다.


    • 이벤트는 직접적인 참조 없이 주로 특정 작업이나 상태 변화에 대한 알림을 처리하는 데 사용됩니다.
    • 이벤트는 디자인 패턴 중 구독자 패턴을 구현하는 데 적합하며 이는 느슨한 결합을 유지하는 중요한 이벤트의 특징 중 하나이다.

 

 

이벤트와 델리게이트의 관계

 

 

이벤트는 델리게이트를 기반으로 동작한다.

 

  • 이벤트를 정의할 때, 해당 이벤트가 어떤 메서드를 호출할 수 있는지를(메서드의 형식) 지정하기 위해 델리게이트를 사용한다.

 

  • 델리게이트는 이벤트의 기반 구조이며 이벤트는 델리게이트를 활용해 특정 작업이 완료되었을 때 이를 알리는 역할만을 수행한다.

 

  • 이벤트는 델리게이트의 확장이라고 볼 수 있으며 이벤트는 델리게이트의 기능을 기반으로 하여, 특정 작업이 발생했을 때 구독자들에게 알릴 수 있는 메커니즘을 제공한다.

 

  • 이벤트는 델리게이트보다 더 제한적이지만, 이를 통해 더 안전한 구조를 제공합니다. (직접적인 참조가 없음)

 

 

 

최종 결론

 

 

  • 델리게이트를 사용하면 좋을 때:
    • 메서드를 인수로 전달하고 싶을 때
    • 런타임에 메서드를 동적으로 호출해야 할 때
    • 여러 메서드를 체인으로 연결해 순차적으로 호출하고 싶을 때
    • 특정 작업이 완료된 후 호출할 콜백 메서드가 필요할 때
  • 이벤트를 사용하면 좋을 때:
    • 특정 작업이나 상태 변화가 발생했을 때 이를 여러 객체에 알리고 반응하게 해야 할 때
    • 객체 간의 느슨한 결합을 유지하면서도 통신이 필요할 때
    • 시스템 내에서 특정 작업의 완료를 알리고 후속 작업을 처리해야 할 때
    • 특정 이벤트에 대해 여러 객체가 동시에 반응해야 할 때

 

 

 

 

사진 출처 : https://www.linkedin.com/pulse/unlocking-power-events-delegates-c-ali-shakkouf-jrlsf/

 

Unlocking the Power of Events and Delegates in C#

Hello again! I'm excited to return with a nice, clear example that will help you understand the power of events and delegates in C#. These tools are essential for building flexible and maintainable applications, and I'm here to show you just how easy and p

www.linkedin.com

 

 

 

728x90
반응형

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

C# - LINQ(Language Integrated Query)  (3) 2024.09.24
C# - 예외 처리 (try ~ catch)  (1) 2024.09.19
C# - 델리게이트(Delagate)  (0) 2024.08.15
C# - 인터페이스(Interface)  (0) 2024.07.19
C# - 라이브러리 vs 프레임워크  (2) 2024.07.11