[이것이 C#이다] 13. 대리자와 이벤트

ssu_hyun·2022년 5월 3일
0

C#

목록 보기
17/22

Key point

  • 대리자
  • 일반화 대리자
  • 익명 메소드
  • 이벤트



13.1 대리자(delegate)

  • 콜백(Callback)
    • 어떤 일을 수행하는 코드
    • 원래 코드가 아닌 다른 코드에게 대신 실행하게 하는 것
    • 프로그램 실행 중에 결정됨(컴파일 시점 X)
  • 대리자(delegate)
    • 콜백을 맡아 일을 실행하는 역할
    • 메소드에 대한 참조
    • 인스턴스가 아닌 형식(Type)
      • int, string과 같은 형식
      • 인스턴스를 따로 만들어야함
    • 선언
       한정자 delegate 반환_형식 대리자_이름(매개변수_목록);

예제

using System;

namespace Delegate
{
	// 1. 대리자 선언
    delegate int MyDelegate( int a, int b);

    class Calculator
    {
        public int Plus(int a, int b)
        {
            return a + b;
        }

        public static int Minus(int a, int b)
        {
            return a - b;
        }
    }

    class MainApp
    {
        static void Main(string[] args)
        {
            Calculator Calc = new Calculator();
            MyDelegate Callback;
			
            // 2. 대리자 인스턴스 생성 & 호출 (참조할 메소드 -> 인수)
            Callback = new MyDelegate( Calc.Plus );  // Plus 참조
            Console.WriteLine(Callback(3, 4));
			
            // 2. 대리자 인스턴스 생성 & 호출 (참조할 메소드 -> 인수)
            Callback = new MyDelegate(Calculator.Minus);  // Minus 참조
            Console.WriteLine(Callback(7, 5));
        }
    }
}

  • 매개변수에 '값'이 아닌 '코드' 자체를 넘기고 싶을 때 대리자 사용

예제

using System;

namespace UsingCallback
{
    delegate int Compare(int a, int b);  // 1. Compare 대리자 선언

    class MainApp
    {	
    	// 2. Compare 대리자가 참조할 비교 메소드 작성
        static int AscendCompare(int a, int b)
        {
            if (a > b)
                return 1;
            else if( a == b )
                return 0;
            else
                return -1 ; 
        }

        static int DescendCompare(int a, int b)
        {
            if (a < b)
                return 1;
            else if (a == b)
                return 0;
            else
                return -1;
        }
		
        // 3. 정렬 메소드 작성 ( 인수 : 정렬할 배열, 비교할 메소드 참조하는 대리자)
        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++)  
                { 
                	// Compare가 어떤 메소드를 참조하느냐에 따라 정렬 결과가 달라짐
                    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, new Compare(AscendCompare));  // 오름차순 정렬

            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, new Compare(DescendCompare));  // 내림차순 정렬           

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

            Console.WriteLine();
        }
    }
}



13.2 일반화 대리자

  • 선언
   delegate int Compare<T>(T a, T b);

예제

using System;

namespace GenericDelegate
{
    delegate int Compare<T>(T a, T b);

    class MainApp
    {
    	// CompareTo() 메소드는 IComparable을 상속해 구현되고
        // 매개변수가 자신보다 크면 -1, 같으면 0, 작으면 1을 반환한다.
        static int AscendCompare<T>(T a, T b) where T : IComparable<T>
        {
            return a.CompareTo(b);
        }

        static int DescendCompare<T>(T a, T b) where T : IComparable<T>
        {
            return a.CompareTo(b) * -1;  // 자신보다 큰 경우 1, 같으면 0, 작은 경우 -1
        }

        static void BubbleSort<T>(T[] DataSet, Compare<T> Comparer)
        {
            int i = 0;
            int j = 0;
            T temp;

            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<int>(array, new Compare<int>(AscendCompare));  // int형 오름차순

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

            string[] array2 = { "abc", "def", "ghi", "jkl", "mno" };

            Console.WriteLine("\nSorting descending...");
            BubbleSort<string>(array2, new Compare<string>(DescendCompare));  // string형 내림차순

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

            Console.WriteLine();
        }
    }
}



13.3 대리자 체인

  • 대리자 하나로 여러 개의 메소드를 동시에 참조하는 것

예제

using System;

namespace DelegateChains
{
	// Notify 대리자 선언
    delegate void Notify(string message);
	
    // Notify 대리자의 인스턴스 EventOccured를 가지는 클래스 Notifier 선언
    class Notifier
    {
        public Notify EventOccured;
    }

    class EventListener
    {
        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!");
        }
    }
}



13.4 익명 메소드(Anonymous Method)

  • 다른 코드 블록에서 재사용될 일이 없는 이름 없는 메소드
  • 대리자 정의 후 이에 맞춰 작성
  • 선언 형식
   대리자_인스턴스 = 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]} ");

            Console.WriteLine();
        }
    }
}



13.5 이벤트(Event)

  • 객체에서 일어난 일, 사건을 표현하는 형식
  • 이벤트 처리기(Event Handler) : 이벤트 발생시 실행되는 메소드
  • event + 대리자

예제

using System;

namespace EventTest
{	
	// 1. 대리자 선언 (클래스 안, 밖 상관X)
    delegate void EventHandler(string message);

    class MyNotifier 
    {
    	// 2. event 한정자로 수식한 대리자 인스턴스 선언
        public event EventHandler SomethingHappened;
        public void DoSomething(int number)
        {
            int temp = number % 10;
			
            // number가 3, 6, 9로 끝나는 값이 될 때마다 이벤트 발생
            if ( temp != 0 && temp % 3 == 0)
            {
                SomethingHappened(String.Format("{0} : 짝", number));
            }
        }
    }

    class MainApp
    {
    	// 3. 이벤트 핸들러 작성(선언한 대리자와 일치하는 메소드)
        static public void MyHandler(string message)
        {
            Console.WriteLine(message);
        }

        static void Main(string[] args)
        {	
        	// 4. 클래스 인스턴스 생성
            MyNotifier notifier = new MyNotifier();
            // 5. SomethingHappened 이벤트에 MyHandler() 메소드를 이벤트 핸들러로 등록
            notifier.SomethingHappened += new EventHandler(MyHandler);

            for (int i = 1; i < 30; i++)
            {
                notifier.DoSomething(i);  // 이벤트가 발생하면 이벤트 핸들러 호출됨
            }
        }
    }
}
  • 이벤트는 public 한정자로 선언돼 있어도 자신이 선언된 클래스 외부에서는 호출이 불가능함 (대리자의 경우 가능)
    • 대리자 : 콜백 용도
    • 이벤트 : 객체의 상태 변화나 사건의 발생을 알리는 용도
delegate void EventHandler(string message);

class MyNotifier
{
    public event EventHandler SomethingHappened;
    
    //...
}

class MainApp
{
    static void Main(string[] args)
    {
        MyNotifier notifier = new MyNotifier();
        // Error!! 이벤트는 객체 외부에서 직접 호출할 수 없다.
        notifier.SomethingHappened("테스트");  
    }
}



연습 문제

  1. 출력 결과가 다음과 같이 나오도록 다음 코드에 익명 메소드를 추가하여 완성하세요.
   7
   2
using System;

namespace Ex13_1
{
    delegate int MyDelegate(int a, int b);
    
    class MainApp
    {
        static void Main(string[] args)
        {
            MyDelegate Callback;
            
            // 익명 메소드 이용
            Callback = delegate (int a, int b)
            		   {
                           return a + b;
                       };
            
            Console.WriteLine(Callback(3, 4));
            
            Callback = delegate (int a, int b)
                       {
                           return a - b;
                       };
                       
            Console.WriteLine(Callback(7, 5));
        }
    }
}
  1. 출력 결과가 다음과 같이 나오도록 다음 코드에 이벤트 처리기를 추가하세요.
축하합니다! 30번째 고객 이벤트에 당첨되셨습니다.
using System;

namespace Ex13_2
{
	// 대리자 선언
    delegate void MyDelegate(int a);
    
    class Market
    {
    	// event 한정자로 대리자 수식하여 선언
        public event MyDelegate CustomerEvent;
        
        public void BuySomething( int CustomerNo )
        {
        	// CustomerNo가 30일 때
            if (CustomerNo == 30)
                CustomerEvent(CustomerNo)
        }
    }
    
    class MainApp
    {
    	// 이벤트 핸들러 작성 (대리자와 일치하는 메소드)
        static public void MyHandler(int a)
        {
            WriteLine($"축하합니다! {a}번째 고객 이벤트에 당첨되셨습니다.");
     
        static void Main(string[] args)
        {
        	// 이벤트 핸들러 등록
            Market market = new Market();
            market.CustomerEvent += new MyDelegate (MyHandler);
            
            // 이벤트 발생
            for (int customerNo = 0; customerNo < 100; customerNo += 10)
                market.BuySomething(customerNo);
        }
    }
}

0개의 댓글