C#

대리자와 이벤트

윤태영(Coding) 2023. 6. 17. 20:54

< 대리자(Delegate) 란? >

대리자는 메서드를 가리키는 타입이다. 그래서 대리자를 통해 메서드를 호출할 수 있다.

병사가 장교에게 전화를 했는데, 장교가 아닌 다른 부사관이 받아서, 부사관이 장교에게 ~사항을 전해달라고 부탁 해서, 그 부사관이 장교에게 전달 사항을 주면, 장교가 다시 병사에게 가서, 전달 사항에 대해 처리를 해주는 상황과 비슷하다. 
예를 들어, "더하기"라는 메서드를 호출하고 싶다면, 대리자를 사용해서 "더하기" 메서드를 가리키고, 대리자를 통해 그 메서드를 실행할 수 있다.

< 대리자 선언, 사용하기 >

ConsoleApp 프로젝트를 만들어서 대리자 선언 및 사용하는 코드

using System;

namespace DelegatesExample
{

	class Program
    {
    	// 대리자를 선언한다. 이 대리자는 두 정수를 받아서, 정수를 반환하는 메서드를 참조할 수 있다.
        public delegate int Calculate(int x, int y);
        
        // 대리자가 참조할 수 있는 형태의 메서드를 만든다.
        public static int Add(int a, int b)
        {
        
        	return a + b;
        }
        
        static void Main(string[] args)
        {
        	// 대리자 인스턴스를 생성하고, Add 메서드를 참조하도록 한다.
            Calculate calculate = Add;
            
            // 대리자 인스턴스를 통해 Add 메서드를 호출한다.
            int result = calculate(3, 4);
            Console.WriteLine($"3 + 4 = {result}");
        }
     }
    }

코드를 실행하면, 아래와 같이, 정상적으로 작동된다.

[일반화 된 대리자 사용하기 ]

Func와 Action은 C#에서 기본적으로 제공하는 일반화된 대리자다. Func는 반환값이 있는 메서드를, Action은 반환값이 없는 메서드를 참조한다.

using System;

namespace DelegatesExample
{

    class Program
    {
        // 대리자를 선언한다. 이 대리자는 두 정수를 받아서, 정수를 반환하는 메서드를 참조할 수 있다.
        public delegate string Calculate(int x, string y);

        // 대리자가 참조할 수 있는 형태의 메서드를 만든다.
        public static string Add(int a, string b)
        {
            return $"{a} + {b}";
        }

        static void Main(string[] args)
        {
            // Func 대리자를 사용하는 예시 
            Func<int, string, string> addFunc = Add;
            string resultFunc = addFunc(5, "감자");
            Console.WriteLine($"5 + 감자 = {resultFunc}");

            // 사용자 정의 대리자를 사용하는 예시
            Calculate calculate = Add;
            string resultCalculate = calculate(5, "감자");
            Console.WriteLine($"5 + 감자 = {resultCalculate}");
        
        }
    }
}

using System;

namespace DelegatesExample
{

    class Program
    {
      
        delegate void MyDelegate(string message);

        static void Main()
        {
            Action<string> myAction = SayHello;
            myAction("Hello with Action!");

            Func<int, int, int> myFunc = Sum;
            int result = myFunc(3, 4);
            Console.WriteLine("Sum with Func: " + result);
        }

        static void SayHello(string message)
        {
            Console.WriteLine(message);
        }

        static int Sum(int a, int b)
        {
            return a + b;
        }

    }
}

< 대리자 체인 >

여러 메서드를 하나의 대리자에 연결하여 한 번에 호출할 수 있다. 이를 대리자 체인이라고 한다.

using System;

namespace DelegatesExample
{

    class Program
    {
      
        delegate void MyDelegate(string message);

        static void Main()
        {
            Action<string> myAction = SayHello;
            myAction += SayGoodbye;

            myAction("태영님");
        }

        static void SayHello(string name)
        {
            Console.WriteLine($"안녕하세요, {name}!");
        }

        static void SayGoodbye(string name)
        {
            Console.WriteLine($"조심히 가십시오, {name}!");
        }

    }
}

< 익명 메서드 >

대리자에 이름이 없는 메서드를 직접 할당할 수도 있다.

using System;

namespace DelegatesExample
{

    class Program
    {

        delegate void MyDelegate(string message);

        static void Main()
        {
            Action<string> myAction = delegate (string message)
            {
                Console.WriteLine(message);
            };

            myAction("반갑습니다!!!");
        }
    }
}

< 이벤트 >

이벤트(Events)
이벤트는 어떤 사건이 발생했을 때 해당 사건을 처리하는 메서드를 실행하는 데 사용된다. 이벤트를 선언할 때는 event 키워드를 사용한다.
.NET Core 또는 .NET 5~6 프로젝트를 만들어서
Program.cs에 아래와 같이 코드를 작성하고 실행해보면, Button 클릭 확인 문구가 출력되는걸 볼 수 있다. 

이 Event 키워드와, 대리자를 사용해서

using System;

namespace EventExample
{
    public class Alarm
    {
        // 대리자 타입 선언
        public delegate void AlarmEventHandler(string message);

        // 이벤트 선언
        public event AlarmEventHandler AlarmEvent;

        // 메서드를 호출하여 알람 이벤트를 발생
        public void RaiseAlarm(string message)
        {
            // 이벤트가 null이 아닌 경우에만 이벤트를 발생시키기
            AlarmEvent?.Invoke(message);
        }
    }

    class Program
    {
        static void Main()
        {
            // Alarm 클래스의 인스턴스를 생성
            Alarm alarm = new Alarm();

            // 알람 이벤트에 대한 처리 로직을 익명 메서드로 등록
            alarm.AlarmEvent += delegate (string message)
            {
                Console.WriteLine($"알람: {message}");
            };

            // 알람 이벤트 발생
            alarm.RaiseAlarm("불이야!");
        }
    }
}

이렇게 작성도 가능하다.