[C# 객체지향] 멤버 유형 확장2_이벤트(event)

eunjin lee·2022년 7월 18일
0

C# 9.0 프로그래밍

목록 보기
16/50

다음 조건을 만족하는 콜백 패턴을 구현하려고 할 때, event 예약어를 사용할 수 있다.

  • 클래스에서 이벤트(콜백)을 제공한다.
  • 외부에서 자유롭게 해당 이벤트를 구독하거나 해지하는 것이 가능하다.
  • 이벤트의 발생은 오직 클래스 내부에서만 가능하다.
  • 이벤트의 첫번째 인자로는 이벤트를 발생시킨 타입의 인스턴스다.
  • 이벤트의 두번째 인자로는 유의미한 값을 제공한다.

다음의 예시를 통해 delegate에서 event로 발전시키는 과정을 소개하겠다.

✍ 샘플코드

    class Test
    {
        static void Main(string[] args)
        {

            PrimeGenerator generator = new PrimeGenerator();

            PrimeGenerator.PrimeDelegate callbackPrint = PrintPrime;
            generator.AddDelegate(callbackPrint);

            PrimeGenerator.PrimeDelegate callbackSum = SumPrime;
            generator.AddDelegate(callbackSum);

            generator.Run(10);

            Console.WriteLine("====================new trial==================");

            generator.RemoveDelegate(callbackSum);
            generator.Run(15);


        }
        //콜백 메서드 1
        static void PrintPrime(int num)
        {
            Console.WriteLine("발견된 소수 :"+num);
        }

        static int sum;
        //콜백메서드 2
        static void SumPrime(int num)
        {
            sum += num;
            Console.WriteLine("누적된 소수의 합 : "+sum);
        }
    }


    class PrimeGenerator
    {
        public delegate void PrimeDelegate(int num);//콜백메서드 묶음을 담을 델리게이트 선언
        PrimeDelegate callbacks; //콜백메서드의 묶음

        public void AddDelegate(PrimeDelegate callback) //구독
        {
            callbacks = Delegate.Combine(callbacks,callback) as PrimeDelegate;
        }

        public void RemoveDelegate(PrimeDelegate callback) //해지
        {
            callbacks = Delegate.Remove(callbacks,callback) as PrimeDelegate;
        }

        public void Run(int limit)
        {
            for(int i=2; i<=limit; i++)
            {
                if(IsPrime(i)&& callbacks != null)
                {
                    callbacks(i); //콜백메서드의 묶음 전부 호출
                }
            }
        }

        public bool IsPrime(int num)
        {
            if((num & 1) == 0)
            {
                return num == 2;
            }

            for(int i=3; (i*i)<=num; i += 2)
            {
                if ((num % i) == 0) return false;
            }

            return num != 1;
        }
    }

✅ 결과

발견된 소수 :2
누적된 소수의 합 : 2
발견된 소수 :3
누적된 소수의 합 : 5
발견된 소수 :5
누적된 소수의 합 : 10
발견된 소수 :7
누적된 소수의 합 : 17
====================new trial==================
발견된 소수 :2
발견된 소수 :3
발견된 소수 :5
발견된 소수 :7
발견된 소수 :11
발견된 소수 :13

이처럼 delegate로도 콜백 메서드 묶음을 정의하고, 이를 구독하거나 해지할 수도 있다. 이제 위의 코드를 event 형식에 알맞게 수정하겠다.

✍ 샘플 코드

    class Test
    {
        static void Main(string[] args)
        {

            PrimeGenerator generator = new PrimeGenerator();

            PrimeGenerator.PrimeDelegate callbackPrint = PrintPrime;
            generator.AddDelegate(callbackPrint);

            PrimeGenerator.PrimeDelegate callbackSum = SumPrime;
            generator.AddDelegate(callbackSum);

            generator.Run(10);

            Console.WriteLine("====================new trial==================");

            generator.RemoveDelegate(callbackSum);
            generator.Run(15);


        }
        //콜백 메서드 1
        static void PrintPrime(object sender, CallbackArg arg)
        {
            Console.WriteLine("발견된 소수 :"+arg.num);
        }

        static int sum;
        //콜백메서드 2
        static void SumPrime(object sender, CallbackArg arg)
        {
            sum += arg.num;
            Console.WriteLine("누적된 소수의 합 : "+sum);
        }
    }

    class CallbackArg
    {
        public int num;
        public CallbackArg(int num)
        {
            this.num = num;
        }
    }

    class PrimeGenerator
    {
        public delegate void PrimeDelegate(object sender, CallbackArg arg);//콜백메서드 묶음을 담을 델리게이트 선언
        PrimeDelegate callbacks; //콜백메서드의 묶음

        public void AddDelegate(PrimeDelegate callback) //구독
        {
            callbacks = Delegate.Combine(callbacks,callback) as PrimeDelegate;
        }

        public void RemoveDelegate(PrimeDelegate callback) //해지
        {
            callbacks = Delegate.Remove(callbacks,callback) as PrimeDelegate;
        }

        public void Run(int limit)
        {
            for(int i=2; i<=limit; i++)
            {
                if(IsPrime(i)&& callbacks != null)
                {
                    callbacks(this, new CallbackArg(i)); //콜백메서드의 묶음 전부 호출
                }
            }
        }

        public bool IsPrime(int num)
        {
            if((num & 1) == 0)
            {
                return num == 2;
            }

            for(int i=3; (i*i)<=num; i += 2)
            {
                if ((num % i) == 0) return false;
            }

            return num != 1;
        }
    }

결과는 첫 번째 샘플 코드와 마찬가지로 나온다. 이제 이 코드를 event를 이용하여 변형하겠다.

    class Test
    {
        static void Main(string[] args)
        {

            PrimeGenerator generator = new PrimeGenerator();

            generator.PrimeGeneratedEvents += PrintPrime;
            generator.PrimeGeneratedEvents += SumPrime;

            generator.Run(10);

            Console.WriteLine("====================new trial==================");

            generator.PrimeGeneratedEvents -= SumPrime;
            generator.Run(15);


        }
        //콜백 메서드 1
        static void PrintPrime(object sender, EventArgs arg)
        {
            Console.WriteLine("발견된 소수 :"+(arg as CallbackArg).num);
        }

        static int sum;
        //콜백메서드 2
        static void SumPrime(object sender, EventArgs arg)
        {
            sum += (arg as CallbackArg).num;
            Console.WriteLine("누적된 소수의 합 : "+sum);
        }
    }

    class CallbackArg : EventArgs
    {
        public int num;
        public CallbackArg(int num)
        {
            this.num = num;
        }
    }

    class PrimeGenerator
    {
        public event EventHandler PrimeGeneratedEvents;
        
        //EventHandler의 선언으로 델리게이트 타입의 선언과 구독, 해지를 작성하지 않아도 되게 되었다.

/*        public delegate void PrimeDelegate(object sender, CallbackArg arg);//콜백메서드 묶음을 담을 델리게이트 선언
        PrimeDelegate callbacks; //콜백메서드의 묶음

        public void AddDelegate(PrimeDelegate callback) //구독
        {
            callbacks = Delegate.Combine(callbacks,callback) as PrimeDelegate;
        }

        public void RemoveDelegate(PrimeDelegate callback) //해지
        {
            callbacks = Delegate.Remove(callbacks,callback) as PrimeDelegate;
        }*/

        public void Run(int limit)
        {
            for(int i=2; i<=limit; i++)
            {
                if(IsPrime(i)&& PrimeGeneratedEvents != null)
                {
                    PrimeGeneratedEvents(this, new CallbackArg(i)); //콜백메서드의 묶음 전부 호출
                }
            }
        }

        public bool IsPrime(int num)
        {
            if((num & 1) == 0)
            {
                return num == 2;
            }

            for(int i=3; (i*i)<=num; i += 2)
            {
                if ((num % i) == 0) return false;
            }

            return num != 1;
        }
    }

즉, 이벤트는 델리게이트의 사용 패턴을 좀 더 일반화해서 제공하는 것이다.

🧐 더 알아보기

  • EventHandler는 public delegate void EventHandler(object sender, EventArgs e)이다.
  • EventHandler 앞에 event 예약어가 없어도 동작에 이상은 없다.
  • 다만 event 예약어가 있으면 클래스 외부에서 이벤트를 호출하는 것을 막을 수 있다. (외부에서는 += 또는 -=를 통해 등록과 해제만 가능하다.)

0개의 댓글