ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 델리게이트, 이벤트 - 1
    @ 16. 1 ~ 17. 1/C# 2016. 11. 18. 05:05

    델리게이트(Delegate)는 사전적 의미로 "대리자, 대표“ 등으로 해석되는데, C/C++에서의 함수 포인터와 비슷한 개념으로 생각하면된다.

    물론, 그 이상의 기능을 한다. 일반적으로 콜백(Call Back) 또는 발생된 이벤트에 대한 처리를 위한 이벤트 핸들러에 사용된다.

    콜백(Call Back)은 호출 될 함수를 알려주어서, 다른 프로그램 혹은 다른 모듈에서 함수를 호출하게 하는 방법이다. 일반적으로 운영체제가 호울할 애플리케이션의 메소드를 지정해서 특정 이벤트가 발생했을 때 호출되도록 지정할 수 있는데, 이런 메소드를 콜백 메소드라고 한다.


    델리게이트의 선언이다. 이것은 함수의 선언과 비슷한 형식으로 되어있다.

    이것은 일종의 함수 타입의 선언이라고 보면 된다. 즉, public 권한을 갖으며, 리턴형은 int이고 int를 매개인자로받는 함수타입을 ShootDelete라는 이름의 델리게이트라고 선언한 것이다.

    일단, 넘어가도록 하자.

    public delegate int ShootDelegate(int n);

    이번엔 델리게이트형으로 멤버를 선언했다. 이것은 바로 윗줄에서 델리게이트로 선언한 함수타입을 사용하는 멤버로 이해하면 될 것이다.

    public ShootDelegate Shoot;

    즉, 다시말해서 여기서의 Shoot는 ShootDelegate에서 선언한것처럼 public 권한을 갖으며, 리턴형은 int이고 int를 매개인자로받는 함수 레퍼런스 멤버로 생각하면 된다.

    여기서, 우리가 ShootDelegate와 Shoot의 관계가 약간 혼동될 수도 있는데, WeaponMode와 m_WeaponMode의 관계처럼, ShootDelegate는 데이터 타입이고, Shoot는 데이터 타입을 사용하는 멤버임에 주의하자.

    public enum WeaponMode
    {
            일반무기,
            특수무기
    }

    if(m_WeaponMode==WeaponMode.일반무기)
                Shoot = new ShootDelegate(Shoot_DDGW);
    else if(m_WeaponMode==WeaponMode.특수무기)
                Shoot = new ShootDelegate(Shoot_DDSW);

    말했듯이 델리게이트는 기본적으로는 함수의 레퍼런스와 같은 역할을 한다. 무기모드가 일반무기일 경우는 Shoot는 Shoot_DDGW() 메소드를 가리키게되는 것이고, 무기모드가 특수무기일 경우에 Shoot는 Shoot_DDSW() 메소드를 가리키게 되는 것이다. 그리고, 주의할 점은, ShootDelegate, Shoot, Shoot_DDGW(), Shoot_DDSW()에서 의미하는 리턴타입과 매개변수타입 등이 같다는 것이다. 



    Shoot_DDGW()와 Shoot_DDSW()가 private이라는 것이다. 즉, 실제 실행되는 메소드를 은닉할 수 있다는 것이다.


    델리게이트(Delegate)의 합성

    자, 아직 약간 혼동이 되긴 하겠지만, 델리게이트의 기본적인 이해는 되었으리라 생각한다.

    하지만, 델리게이트의 기능은 여기서 끝나지 않는다. 델리게이트의 또 다른 특성인 델리게이트의 합성에 대해 살펴보자.

    우리가 비행기 슈팅게임을 하다보면, 여러 번 파워업 하게되고, 그렇게 되면, 무기발사버튼을 한 번 누르면, 여러 개의 기본 총알과 미사일등 동시에 여러 무기가 발사되는 것을 본적이 있을 것이다. 자, 이것을 델리게이트의 합성으로 구현해 보겠다.

    Shoot = new ShootDelegate(Shoot_DDGW)+new ShootDelegate(Shoot_DDSW);





    이벤트

    주의 깊게 봐야할 것은 “:EventArgs" 부분인데, 이것은 EventArgs 클래스를 상속받아서 사용한다는 것


    다음은 델리게이트의 선언부분이다. 이벤트에 사용될 델리게이트는 두 개의 매개인자를 사용하는데, 하나는 이벤트를 발생시키는 객체를 나타내는 object 형의 변수이며, 또 다른 하나는 EventArgs를 상속받은 클래스의 객체이다.

            // 이벤트 핸들러를 위한 델리게이트 작성
            public delegate void RequestRepairEventHandler
                                            (object source, RobotMessageEventArgs e);

    다음은 위에서 만든 델리게이트 형으로 이벤트 핸들러를 선언한 것이다. 여기에서는 event라는 키워드를 사용했다는 것을 주목하자.

            // RequestRepairEventHandler 형으로 이벤트가 
            //발생되었을때 호출될 메소드
            public event RequestRepairEventHandler
                                             OnRequestRepairEventHandler;




    이벤트를 발생시킬 때 사용되는 매개인자는 두 개가 필요하다. 이벤트를 보낼 객체는 로봇 자신이므로 this 키워드를 사용하고, 이벤트의 매개인자로 사용될 RobotMessageEventArgs 객체를 생성한다. 그리고 이벤트 핸들러인 OnRequestRepairEventHandler를 this 키워드와 이벤트 매개인자 e를 이용해서 호출한다.

    //로봇이 손상을 입었음
    public void OnDamaged()
    {
            Console.WriteLine("Robot : 로봇이 손상을 입었습니다.");
     
            RobotMessageEventArgs e=new 
                            RobotMessageEventArgs("박사님, 수리해주세요");
            OnRequestRepairEventHandler(this, e);
     
            Console.WriteLine("Robot : 김박사에게 수리를 요청했습니다.");
    }



    어 그러니까 정리하면 ...

    옵저버 패턴과 비슷한거넫..이벤트는 이벤트일뿐...델리게이트랑은 직접적 연관은 없는데..

    어..그러니까 델리게이트는 함수 포인터 역활이고..

    이 델리게이트를 실행해주기 위한것이 이벤트일 뿐.....그래서 이벤트에 델리게이트를 등록하는 것임.

    public delegate void MyEventHandler();    //이벤트 처리를 위한 델리게이트를 정의한것..

    public event MyEventHandler MyEvent;    //이게 이벤트에 델리게이트를 등록한거임..


    [C# 이벤트, 델리게이트]이벤트처리, Delegate, EventHandler


    윈도우 폼등에서 특정 사건(Event, 마우스 클릭등)가 발생되면 인터럽트가 발생되고 이에따라 윈도우 메시지가 발행되어 이를 C#에 전달해 주는데, C# 클래스내에는 이 이벤트를 통해 어떤 사건(Event, 윈도우 버튼의 클릭등)이 일어 났을 때 외부에 있는 이벤트 가입자에게 알려준다.

    닷넷 이벤트 모델은 디자인 패턴의 옵저버 패턴과 비슷한데, 어떤 객체에 관찰자(옵저버)를 등
    록하고 객체의 상태가 변화하면 옵저버로 통지(Notify)해 주는 데 이때 어떤 객체는 
    발행자(Publisher)가 되고 옵저버 객체들은 구독자(Subscriber)가 되는 것이다.

    닷넷에서 이벤트는 event 라는 키워드를 통해 생성하며 외부 클래스에서는 호출이 불가능하다.
    (델리게이트는 가능)

    이벤트에 가입하는 외부 가입자에서 이벤트가 발생했을 때 어떤 메소드를 실행할 지 지정하는 데 이를 이벤트 핸들러 라고 하며 이벤트에 가입하기 위해서는 += 연산자를 사용하며 삭제를 위해서는 -= 연산자를 이용한다. 그러므로 하나의 이벤트에 여러개의 이벤트 핸들러(이벤트 처리 메소드)를 추가 할 수 있으며 이벤트 발생하게 되면 순서대로 호출된다.

    먼저 간단히 이벤트를 만들어서 이를 테스트 해보자.

    1. 게시지, 구독자를 통한 간단한 테스트

    using System;

    namespace ConsoleApplication1
    {
        //이벤트 게시자 클래스
        //이벤트를 발생시키는 객체
        class EventPublisher
        {
            public delegate void MyEventHandler();    //이벤트처리를위한델리게이트정의
            public event MyEventHandler MyEvent;      //이벤트 정의
            public void Do()
            {
                //이벤트 가입자가 있는지 확인
                if (MyEvent != null) {
                    MyEvent(); //이벤트 발생
                }     
            }
        }

        //구독자 클래스
        class Subscriber
        {
            static void Main(string[] args)
            {
                EventPublisher p = new EventPublisher();

                //구독자를 등록
                //이벤트를 발갱하면 호출될 메소드를 정의, doAction 메서드 호출
                //MyEvent라는 이벤트에 이벤트핸들러인 델리게이트를 등록
                //MyEvent가 발생되면 델리게이트가 참조하는 메소드 doAction() 실행
                p.MyEvent += new EventPublisher.MyEventHandler(doAction);

                //테스트를 위해, 이벤트 발생
                //실제 윈폼등에서는 버튼등을 클릭하면 자동으로 Publisher에서 이벤트가 발생한다.
                p.Do();
            }
            //MyEvent라는 이벤트가 발생하면 호출되는 메서드
            static void doAction()
            {
                Console.WriteLine("MyEvent라는 이벤트 발생...");
            }
        }

    }


    2. System.EventHandler 델리게이트를 통한 이벤트 처리

    이번에는 System.EventHandler라는 C#에서 제공하는 델리게이트를 이용해 보자.

    EventHandler 클래스는 이벤트를 위한 델리게이트로 윈도우폼 버턴 객체의 Click 이벤트등에서 사용되는 델리게이트이다. (이벤트 발생시 전달할 데이터가 없는 경우에 주로 사용)

    EventHandler 클래스의 생김새는 다음과 같다.

        public delegate void EventHandler(Object sender,EventArgs e)  
         sender : 이벤트가 발생한 객체, Publisher자신의 참조
         e : 이벤트 발생시 전달할 데이터, 만약 전달할 데이터가 있는 경우 EventArgs 클래스를 상속하여 정의한다.
    그러므로 이 델리게이트가 참조하는 메소드인 doAction 메소드 역시 이러한 모습이어야 한다.


    using System;
    namespace ConsoleApplication1
    {
        
        //이벤트 게시자 클래스
        class EventPublisher
        {
            //public delegate void MyEventHandler();    //이벤트처리를위한델리게이트정의
            public event EventHandler MyEvent;      //이벤트 정의
            public void Do()
            {
                //이벤트 가입자가 있는지 확인
                if (MyEvent != null)
                {
                    MyEvent(null, null); //이벤트 발생
                }   
            }
        }

        //구독자 클래스
        class Subscriber
        {
            static void Main(string[] args)
            {
                EventPublisher p = new EventPublisher();

                //구독자를 등록
                //이벤트를 발갱하면 호출될 메소드를 정의, doAction 메서드 호출
                //MyEvent라는 이벤트에 이벤트핸들러인 델리게이트를 등록
                //MyEvent가 발생되면 델리게이트가 참조하는 메소드 doAction() 실행
                p.MyEvent += new EventHandler(doAction);

                //테스트를 위해, 이벤트 발생
                //실제 윈폼등에서는 버튼등을 클릭하면 자동으로 Publisher에서 이벤트가 발생한다.
                p.Do();           
            }

            //MyEvent 이벤트가 발생하면 호출되는 메서드
            //System.EventHandler의 기본형태가 메개변수 2개받고, 
            //리턴형이 void형임로 같은 형태로 만든다.
            static void doAction(object sender, EventArgs e)
            {
                Console.WriteLine("MyEvent라는 이벤트 발생...");
            }
        }

    }


    3. 이번에는 이벤트가 발생할 때 데이터를 넘기도록 예제를 작성해 보자.

    일반적으로 이벤트가 발생할 때 전달해야하는 데이터가 존재한다. 예를들어 마우스를 클릭한다면 어떤 버튼이 몇번 눌러졌는지등...


    using System;
    namespace ConsoleApplication1
    {

        //이벤트 발생시 넘길 데이터(System.EventArgs에서 파생)
        class EventPublisherArgs : System.EventArgs
        {
            public string myEventData;

            public EventPublisherArgs(string myEventData)
            {
                this.myEventData = myEventData;
            }
        }


        //이벤트 게시자 클래스
        class EventPublisher
        {
            //이벤트처리를위한델리게이트정의
            public delegate void MyEventHandler(object sender, EventPublisherArgs e);
            public event MyEventHandler MyEvent;      //이벤트 정의        

            public void Do()
            {
                //이벤트 가입자가 있는지 확인
                if (MyEvent != null)
                {
                    EventPublisherArgs args = new EventPublisherArgs("데이터");
                    MyEvent(this, args); //이벤트 발생
                }
            }
        }

        //구독자 클래스
        class Subscriber
        {
            static void Main(string[] args)
            {
                EventPublisher p = new EventPublisher();

                //이벤트가 발생하면 doAction 메서드 호출
                //MyEvent라는 이벤트에 이벤트핸들러인 EventHandler를 등록
                //MyEvent가 발생되면 델리게이트가 참조하는 메소드 doAction() 실행
                p.MyEvent += new EventPublisher.MyEventHandler(doAction);

                //테스트를 위해, 이벤트 발생
                //실제 윈폼등에서는 버튼등을 클릭하면 자동으로 Publisher에서 이벤트가 발생한다.
                p.Do();
            }

            //MyEvent 이벤트가 발생하면 호출되는 메서드
            //EventPublisherArgs가 이벤트 발생시 넘어오는 데이터이다.
            static void doAction(object sender, EventPublisherArgs e)
            {
                Console.WriteLine("MyEvent라는 이벤트 발생...");
                Console.WriteLine("이벤트 매개변수 : " + e.myEventData);
            }
        }

    }


    이벤트를 정리합니다..

    이벤트는 델리게이트를 통해서 이벤트 호출이 가능하다.. 델리게이트는 함수포인터 같은것이다..

    그래서..단순히 델리게이트가 없다고 해서 이벤트 호출이 안되는것 아니다..

    이벤트는 이벤트를 실행해주는것..즉 델리게이트에 물린 함수를 실행...델리게이트에 함수를 먼저 등록해야한다.


    이벤트 핸들러에 직접 함수를 등록할수도 있다. 



    '@ 16. 1 ~ 17. 1 > C#' 카테고리의 다른 글

    가비지 컬렉터 가비지 컬렉션  (0) 2016.11.19
    C#AVL 트리  (0) 2016.11.18
    object 란? boxing unboxing!  (0) 2016.11.18
    참조형식과 값형식 차이, 함수 매개변수(in, out, ref)  (0) 2016.11.18
    getter setter  (0) 2016.11.18
Designed by Tistory.