본문 바로가기
전공/C# 프로그래밍

16강. 대리자와 이벤트 (2)

by 임 낭 만 2023. 9. 11.

대리자 체인

대리자 인스턴스의 메소드 참조

  • C# 2.0부터는 대리자 인스턴스에 메소드를 쉽게 연결하도록 지원
    • 기존에는 new 연산자를 사용하고 생성자의 인수로 메소드 이름을 입력
    • C# 2.0부터는 ‘=’ 연산자를 사용해 대리자 인스턴스에 메소드를 직접 대입 가능

대리자 체인

  • 대리자 하나가 여러 개의 메서드를 동시에 참조
    • ‘+=’ 연산자를 이용하여 대리자 인스턴스에 여러 메서드를 결합 가능

  • 대리자 하나가 여러 개의 메서드를 동시에 참조
    • ‘+=’ 연산자가 아니어도 다음의 방법들로 대리자 체인 생성 가능

  • 대리자 체인에서 특정 대리자를 끊어낼 때는 ‘-=’ 연산자를 이용

using System;

namespace DelegateChains
{
    delegate void Notify(string message);
    class Notifier
    {
        public Notify EventOccured;	//Notify 대리자 선언
    }
    class EventListener	//Notify 대리자의 인스턴스
    {		//EventOccured를 가지는 클래스 Notifier 선언
        private string name;
        public EventListener(string name)
        {
            this.name = name;
        }
        public void SomethingHappend(string message)
        {
            Console.WriteLine($"{name}.SomethingHappened : {message}");
        }
    }
    class MainApp
    {
        static void Main(string[] args)
        {
            Notifier notifier = new Notifier();
            EventListener listener1 = new EventListener("Listener1");
            EventListener listener2 = new EventListener("Listener2");
            EventListener listener3 = new EventListener("Listener3");
		//+=연산자를 이용한 체인 만들기
            notifier.EventOccured += listener1.SomethingHappend;
            notifier.EventOccured += listener2.SomethingHappend;
            notifier.EventOccured += listener3.SomethingHappend;
            notifier.EventOccured("You've got mail.");

            Console.WriteLine();
		//-= 연산자를 이용한 체인 끊기
            notifier.EventOccured -= listener2.SomethingHappend;
            notifier.EventOccured("Download complete.");

            Console.WriteLine();
		//+, = 연산자를 이용한 체인 만들기
            notifier.EventOccured = new Notify(listener2.SomethingHappend) 
                                  + new Notify(listener3.SomethingHappend);
            notifier.EventOccured("Nuclear launch detected.");

            Console.WriteLine();

            Notify notify1 = new Notify(listener1.SomethingHappend);
            Notify notify2 = new Notify(listener2.SomethingHappend);
		// Delegate.Combine() 메서드를 이용한 체인 만들기
            notifier.EventOccured =
                (Notify)Delegate.Combine( notify1, notify2);
            notifier.EventOccured("Fire!!");

            Console.WriteLine();
		// Delegate.Remove() 메서드를 이용한 체인 끊기
            notifier.EventOccured = 
                (Notify)Delegate.Remove( notifier.EventOccured, notify2);                
            notifier.EventOccured("RPG!");
        }
    }
}


익명 메서드

익명 메서드

  • 이름이 없는 메서드
    • delegate 키워드를 사용해 선언하고 대리자 인스턴스에 연결 가능
    • 익명 메서드는 자신을 참조할 대리자와 동일한 반환/매개변수 형식 사용

using System;

namespace AnonymouseMethod
{
    delegate int Compare(int a, int b);
    class MainApp
    {
        static void BubbleSort(int[] DataSet, Compare Comparer)
        {
            int i = 0;
            int j = 0;
            int temp = 0;

            for (i = 0; i < DataSet.Length - 1; i++)
            {
                for (j = 0; j < DataSet.Length - (i + 1); j++)
                {
                    if (Comparer(DataSet[j], DataSet[j + 1]) > 0)
                    {
                        temp = DataSet[j + 1];
                        DataSet[j + 1] = DataSet[j];
                        DataSet[j] = temp;
                    }
                }
            }
        }
        static void Main(string[] args)
        {
            int[] array = { 3, 7, 4, 2, 10 };

            Console.WriteLine("Sorting ascending...");
            BubbleSort(array, delegate(int a, int b)
                              {
                                if (a > b)
                                    return 1;
                                else if (a == b)
                                    return 0;
                                else
                                    return -1;
                              });

            for (int i = 0; i < array.Length; i++)
                Console.Write($"{array[i]} ");

            int[] array2 = { 7, 2, 8, 10, 11 };
            Console.WriteLine("\nSorting descending...");
            BubbleSort(array2, delegate(int a, int b)
                               {
                                 if (a < b)
                                     return 1;
                                 else if (a == b)
                                     return 0;
                                 else
                                     return -1;
                               });

            for (int i = 0; i < array2.Length; i++)
                Console.Write($"{array2[i]} ");
        }
    }
}


이벤트

이벤트

  • WinForm에서 버튼 클릭, 콤보 박스에서 값 선택 등 발생한 사건을 알리는 것을 의미
  • 이벤트는 대리자를 event 한정자로 수식해서 생성
    • 이벤트를 선언하고 사용하는 방법을 예제를 통해 보면,

using System;

namespace EventTest
{
    delegate void EventHandler(string message);
    class MyNotifier 
    {
        public event EventHandler SomethingHappened;
        public void DoSomething(int number)
        {
            int temp = number % 10;

            if ( temp != 0 && temp % 3 == 0)
            {
                SomethingHappened(String.Format("{0} : 짝", number));
            }
        }
    }
    class MainApp
    {
        static public void MyHandler(string message)
        {
            Console.WriteLine(message);
        }
        static void Main(string[] args)
        {
            MyNotifier notifier = new MyNotifier();
            notifier.SomethingHappened += new EventHandler(MyHandler);

            for (int i = 1; i < 30; i++)
            {
                notifier.DoSomething(i);
            }
        }
    }
}

대리자와 이벤트

  • 이벤트는 단지 대리자를 event 키워드로 수식해서 선언한 것을 의미
  • 대리자와 다른 이벤트의 특징
    • 이벤트가 선언된 클래스 밖에서 직접 이벤트 호출 불가 (컴파일 에러 발생)
    • 단, 클래스 밖에서 해당 이벤트에 이벤트 핸들러 등록 또는 해지는 가능

.NET에서 지원하는 이벤트 처리

  • .NET에서 이미 정의된 이벤트 대리자 EventHandler 사용하여 이벤트 생성 가능
    • 클래스에서 EventHandler 를 이용해 이벤트 선언
    • 클래스 외부에서 자유롭게 이벤트를 처리하기 위한 메서드 등록 및 해지 가능
    • 주의 할 점은 이벤트 발생은 오직 이벤트가 선언된 클래스 내부에서만 가능
    • 이벤트 대리자 EventHandler의 첫 번째 매개변수는 이벤트를 발생시킨 타입의 인스턴스이고, 두 번째 매개변수는 .NET에서 이미 정의된 System.EventArgs 타입의 이벤트에 속한 값

using System;

namespace Event
{
    class PrimeCallbackArg : EventArgs
    {// 이벤트에 속한 값을 담는 클래스 정의
        public int Prime;
        public PrimeCallbackArg(int prime)
        {
            this.Prime = prime;
        }
    }
    class PrimeGenerator
    { // C#에서 정의된 EventHandler를 이용해 이벤트 선언
        public event EventHandler PrimeGenerated;
        public void Run(int limit)
        {
            for (int i = 2; i <= limit; i++)
            {// 소수의 경우 이벤트 발생
                if (IsPrime(i) == true && PrimeGenerated != null)
                    PrimeGenerated(this, new PrimeCallbackArg(i));
            }
        }
        private bool IsPrime(int candidate)
        {// 매개변수로 입력 받은 정수 candidate이 소수인지 판별
            if ((candidate & 1) == 0)
                return candidate == 2;
            for (int i = 3; (i * i) <= candidate; i += 2)
            {
                if ((candidate & i) == 0) return false;
            }
            return candidate != 1;
        }
    }
    class MainApp
    {
        static void PrintPrime(object sender, EventArgs arg)
        { // 이벤트를 처리하기 위한 메소드 1
            Console.Write((arg as PrimeCallbackArg).Prime + ", ");
        }
        static int Sum;
        static void SumPrime(object sender, EventArgs arg)
        { // 이벤트를 처리하기 위한 메소드 2
            Sum += (arg as PrimeCallbackArg).Prime;
        }
        static void Main(string[] args)
        {
            PrimeGenerator gen = new PrimeGenerator();
            gen.PrimeGenerated += PrintPrime;
            gen.PrimeGenerated += SumPrime;
            gen.Run(10);
            Console.WriteLine();
            Console.WriteLine(Sum);
            gen.PrimeGenerated -= SumPrime;
            gen.Run(15);
        }
    }
}

 

 

댓글