Creative Commons License

Microsoft .NET

닷넷!시작하기
닷넷! Ver 2.0~
닷넷!스킬업
웹개발
윈폼개발
실용모듈개발
Tip & Tech
하루 한 문법

Microsoft .NET 개발자들을 위한 공간입니다. 기초강의에서 부터 고급 기술 정보 및 팁등을 다루도록 하겠습니다.

.

닷넷!시작하기

Microsoft. NET 을 시작하는 분들을 위한 강좌입니다. 주로 기초적인 내용과 때론 기본적인 내용을 다룹니다

[C# 기초강좌] 17. C# 이벤트(Event)

작성자 : 박종명
최초 작성일 : 2010-07-02 (금요일)
최종 수정일 : 2010-07-02 (금요일)
조회 수 : 13299

이벤트는 발행자 ? 구독자 모델입니다

 

안녕하세요. 박종명입니다. 닷넷 열일곱 번째 강좌를 진행하도록 하겠습니다

지난 강좌에서 델리게이트에 대해 알아보았는데요. 이번 강좌 주제인 이벤트(Event)는 델리게이트와 깊은(?)
연관이 있습니다. 그럼 자세히 알아보도록 하겠습니다

 

 

일상적으로 이벤트(Event)라 함은 행사라는 의미에 가깝습니다

그러나 프로그래밍에서의 이벤트는 어떠한 사건, 특정 상황(의 발생)이라는 의미가 강합니다

 

예를 들어 폼(Form) 객체의 버턴이 클릭되었다던지 객체의 상태가 변경되었다던지 하는 프로그램의 실행 중 발생하는 사건을 이벤트라 하며 이러한 이벤트를 다른 객체에서 통지(Notify)받아 적절히 처리하는 형태를
이벤트 기반 프로그래밍 모델이라 할 수 있겠습니다.

 

닷넷은 이벤트 기반 프로그래밍 모델을 지원합니다.

경험의 재사용이라 일컬어지는 디자인 패턴 중 옵저버 패턴(observer pattern)이 있습니다

옵저버 패턴은 특정 객체에 관찰자(옵저버)를 등록하고 그 객체의 상태가 변화하면 옵저버로 통지해 주는 패턴입니다. 이 때 객체는 발행자가 되고 옵저버 객체들은 구독자가 되는 셈이지요

 

닷넷의 이벤트 모델은 옵저버 패턴에서의 구독자-발행자모델과 개념적으로 거의 동일하다고 보면 되겠습니다.



 

닷넷의 이벤트 모델은 개념적으로 자바의 이벤트와 옵저버 패턴과 개념적으로 유사합니다.

다만 구현 방식이나 구현 모델의 차이는 있습니다

 

 

델리게이트와 이벤트(Delegate & Event)

닷넷의 이벤트는 앞 강좌에서 살펴 본 델리게이트를 이용합니다

델리게이트는 함수(메서드)의 간접참조를 유지하면서 실행 시 함수를 간접적으로 호출하는 기법입니다

 

이벤트는 특정 객체에서 다른 객체로 자신의 상태 변화를 알려 주는 것인데 이 때 알려 주는 대상이 함수가 됩니다.
A 객체의 상태 변화 시 B 객체로 통지하는데 A 객체에서 B 객체의 함수를 호출하는 것과 같은 이치 입니다.
이때 A 객체가 B 객체의 함수를 호출할 때 델리게이트가 이용된다는 것입니다

 

다시 말해 이벤트가 발생하여 이를 다른 객체에게 알려주는 행위라는 것이 결국에는 그 객체의 함수를 호출하는 것이 되며 이 함수는 이벤트를 발생 시킨 객체와는 다른 객체에 있기 때문에 위임(delegate) 모델을 이용하여 간접 호출을 하게 되는 것입니다

 

 

구독자와 발행자

이벤트 모델에서는 두 가지 역할이 존재합니다

 

발행자(Publisher)

구독자는 이벤트를 발생시키는 객체가 됩니다. 즉 상태 정보를 가지고 있는 관심 대상 객체입니다

 

구독자(Subscriber)

구독자의 상태 변화를 예의 주시(?)하는 객체가 됩니다. 구독자 객체에서 이벤트가 발생하면 이 객체의 특정 함수가
실행되도록 이벤트를 구독합니다

 


간단한 이벤트 데모

그럼 더 알아보기 전에 간단한 이벤트 데모를 보겠습니다

먼저 아래와 같이 발행자(Publisher) 클래스를 정의합니다. 발행자는 Click 라는 이벤트를 가지고 있습니다

 

class Publisher{

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

 

    public event MyEventHandler Click;     //이벤트 정의

 

    public void DoClick(){

        if(Click != null) //이 객체의 이벤트를 구독하는 구독자가 있는지 검사

        {

            Click(); //이벤트 발생. 구독자객체에게 이벤트를 통지함.(델리게이트 호출)

        }

    }

}

 

다음으로 구독자(Subscriber) 클래스를 정의합니다. 구독자는 발행자의 Click 이벤트를 구독하도록 합니다

 

class Program{       

    static void Main(string[] args){

        //이벤트 발행자 객체 생성

        Publisher p = new Publisher();

 

        //이벤트 구독. 이벤트 구독자는 현재 객체이다

        //이벤트가 발생하면 p_Click 메서드를 호출하라.. 라는 의미

        //이벤트 핸들러를 등록한다.. 라고 표현합니다

        p.Click += new Publisher.MyEventHandler(p_Click);

 

        //테스트를 위해 이벤트 발생. 실제 환경에서는 발행자에서 자동 이벤트 발생.

//구독자는 구독에만 신경 쓰면 됨

        p.DoClick();

    }

 

    //Publisher 객체에서 Click 이벤트가 발생하면 호출되는 메서드

    static void p_Click(){

        Console.WriteLine("Publisher 객체의 Click 이벤트가 발생하였습니다");

    }               

}

 

이벤트는 event 라는 키워드를 통해 선언하게 됩니다. 이 때 미리 정의한 델리게이트가 사용됩니다

그리고 이전 강좌에서 설명했듯이 모든 델리게이트는 MuliticastDelegate 클래스로부터 상속받기 때문에 하나의 이벤트에 여러 개의 핸들러 메서드를 등록할 수 있습니다

+= 연산자로 이벤트를 구독하고 -= 연산자로 구독을 취소할 수 있습니다

 

주석을 통해 코드 설명을 했습니다만 간단히 다시 요약하면 다음과 같은 과정입니다

이 데모는 간단한 이벤트 흐름과 사용 방법에 대해 알아보기 위한 코드입니다

주석에도 설명했지만, 구독자에서 직접 이벤트를 발생시키는 기이한(?) 구조입니다

 

대부분의 경우 이벤트 객체 자체에서 이벤트를 발생시키며 구독자는 구독에만 신경 쓰면 되는 구조입니다

윈폼 응용프로그램의 Button 객체는 Click 이벤트를 정의하고 있는데 사용자가 버턴을 클릭하면 Button 자체에서
이벤트를 발생 시키듯이요

 

 

System.EventHandler

앞서 데모에서 이벤트를 위해 델리게이트를 정의하였습니다

통상 이런 식으로 많이 사용하지만 경우에 따라서는 닷넷에 미리 정의된 이벤트를 위한 델리게이트를 이용해도 무방합니다대표적으로 System.EventHandler 이 있는데요

앞 예제의 Publisher 클래스에 정의한 MyEventHandler 델리게이트 대신 닷넷의 EventHandler 을 이용하도록 코드를 변경해 보겠습니다

 

class Publisher{      

 public event EventHandler Click; //닷넷에 미리 정의된 System.EventHandler 델리게이트 이용

 

 public void DoClick()

 {

     if(Click != null)

     {

         Click(null,null); //System.EventHandler은 두개의 매개변수를 요구함(일단 null 처리)

     }

 }

}

 

그리고 구독자 클래스에서 다음과 같이 이벤트 핸들러를 등록하면 됩니다

p.Click += new System.EventHandler(p_Click);

 

이제 코드는 앞서 예제와 동일하게 동작합니다

 

System.EventHandler 클래스는 이벤트를 위한 델리게이트로 윈폼의 버턴 객체의 Click 이벤트에 이용되는
델리게이트이기도 합니다. 이벤트 발생 시 특별히 전달할 데이터가 없는 경우에 사용하는 델리게이트 입니다

 

 

이벤트 데이터(이벤트 매개변수)

이벤트 발행과 구독에 대한 데모를 살펴 보았습니다

그런데 앞 두 예제에서는 이벤트 발생 시 전달하는 데이터가 없습니다. 이벤트 발생 코드는 아래와 같습니다

 

Click();

 

Click(null,null); //System.EventHandler은 두개의 매개변수를 요구함(일단 null 처리)

 

이벤트가 발생할 때 전달할 데이터가 없는 경우도 있지만 일반적으로는 상태 정보를 반환하는 데이터가 있기 마련입니다

객체의 상태라는 것은 결국 어떤 속성 값이 가능성이 크며 그 값의 변화를 구독자에게 알려 주는 것이 보다 일반적입니다. 즉 앞 예제에서는 Click(상태 값) 의 형태로 이벤트를 발생 시키는 것이 일반적입니다

 

델리게이트의 시그너처를 임의로 변경하여 이벤트 매개변수를 지정할 수 있지만 닷넷에서는 이를 위한 특정 규칙을 만들어 놓고 이 규칙을 이용하기를 권장합니다

 

앞 예에서 EventHandler 델리게이트를 사용했는데요. 이 델리게이트의 시그너처는 다음과 같이 정의되어 있습니다

public delegate void EventHandler(Object sender,EventArgs e)

 

매개변수로 Object 타입과 EventArgs, 두 개를 정의하고 있습니다.

 

Object로 정의된 sender 에는 이벤트를 발생시킨 자신 즉 발행자 자신의 참조를 담도록 합니다

그리고 EventArgs 타입의 e 에는 이벤트 발생 시 발행자에게 전달한 데이터를 담도록 하는데 이 데이터를 정의하는 객체의 기본 클래스입니다. 만일 전달할 데이터가 있는 경우 EventArgs 클래스를 상속하여 정의하도록 권장하는 것입니다

 

그럼 앞서 작성해 본 데모를 매개변수가 있는 이벤트로 그것도 닷넷에서 권장하는 방식으로 변경해 보도록 하겠습니다

 

우선 이벤트데이터를 위한 클래스를 정의합니다. System.EventArgs 로부터 상속 받도록 합니다

 

//이벤트 데이타를 위한 클래스 정의(System.EventArgs로 부터  상속 받음)

class PublisherEventArgs : System.EventArgs{

    public string eventData;

 

    public PublisherEventArgs(string eventData){

        this.eventData = eventData;

    }

}

 

그리고 구독자 객체의 델리게이트에 매개변수를 정의하고 이벤트 발생 시 매개변수 정보들을 같이 전달하도록 합니다

class Publisher{

    //매개변수 정보를 추가하여 델리게이트를 정의한다

    public delegate void MyEventHandler(object sender, PublisherEventArgs e);

 

    public event MyEventHandler Click;

 

    private string data; //실제 관심있는 발행자의 상태 데이타

 

    public void DoClick(){

        if (Click != null) //이 객체의 이벤트를 구독하는 구독자가 있는지 검사

        {

            this.data = "이벤트 데이터입니다"; //임의로 상태 정보 설정

 

            //이벤트데이타를 위한 객체생성

            PublisherEventArgs pArgs = new PublisherEventArgs(this.data);

 

            Click(this, pArgs); //매개변수정보와 함께 이벤트 통지함

        }

    }

}

 

마지막으로 구독자의 이벤트 핸들러 메서드에서 이를 출력하도록 합니다

 

static void Main(string[] args){          

    Publisher p = new Publisher();

       

    p.Click += new Publisher.MyEventHandler(p_Click);

          

    p.DoClick();

}

 

static void p_Click(object sender, PublisherEventArgs e){

    Console.WriteLine(e.eventData); //이벤트 데이터를 출력한다

}

 

 

참고로 닷넷에서는 많은 종류의 이벤트가 있습니다.

버턴 이벤트, 체크박스 이벤트, 네비게이터 이벤트, 로드 이벤트 등

이런 각각이 이벤트는 그 특징에 따라 이벤트 데이터를 상세하게 지원합니다.

예를 들어 어셈블리가 로딩 될 때 발생하는 AssemblyLoad 이벤트에는 AssemblyLoadEventArgs 매개변수를 이용하는데 이 객체에는 로드 되는 어셈블리를 나타내는 Assembly 객체를 반환하는 속성을 제공합니다

 

다음 표는 System.EventArgs 를 상속하는 다양한 이벤트 매개변수 클래스들을 나타냅니다

 

System.EventArgs
    System.Collections.Specialized.NotifyCollectionChangedEventArgs
    System.ComponentModel.AsyncCompletedEventArgs
    System.ComponentModel.CancelEventArgs
    System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs
    System.ComponentModel.Composition.Hosting.ExportsChangeEventArgs
    System.ComponentModel.CurrentChangingEventArgs
    System.ComponentModel.DataErrorsChangedEventArgs
    System.ComponentModel.DoWorkEventArgs
    System.ComponentModel.ProgressChangedEventArgs
    System.ComponentModel.PropertyChangedEventArgs
    System.Data.Services.Client.ReadingWritingEntityEventArgs
    System.Data.Services.Client.SendingRequestEventArgs
    System.Net.Sockets.SocketAsyncEventArgs
    System.Net.WriteStreamClosedEventArgs
    System.ResolveEventArgs
    System.Runtime.InteropServices.Automation.AutomationEventArgs
    System.ServiceModel.UnknownMessageReceivedEventArgs
    System.UnhandledExceptionEventArgs
    System.Windows.ApplicationUnhandledExceptionEventArgs
    System.Windows.Browser.HtmlEventArgs
    System.Windows.CheckAndDownloadUpdateCompletedEventArgs
    System.Windows.Controls.DataGridCellEditEndedEventArgs
    System.Windows.Controls.DataGridColumnEventArgs
    System.Windows.Controls.DataGridPreparingCellForEditEventArgs
    System.Windows.Controls.DataGridRowClipboardEventArgs
    System.Windows.Controls.DataGridRowDetailsEventArgs
    System.Windows.Controls.DataGridRowEditEndedEventArgs
    System.Windows.Controls.DataGridRowEventArgs
    System.Windows.Controls.DataGridRowGroupHeaderEventArgs
    System.Windows.Controls.DatePickerDateValidationErrorEventArgs
    System.Windows.Controls.FocusingInvalidControlEventArgs
    System.Windows.Controls.NotifyEventArgs
    System.Windows.Controls.Primitives.ItemsChangedEventArgs
    System.Windows.Data.FilterEventArgs
    System.Windows.Input.TouchFrameEventArgs
    System.Windows.Interop.NavigationStateChangedEventArgs
    System.Windows.Media.Imaging.DownloadProgressEventArgs
    System.Windows.Media.RenderingEventArgs
    System.Windows.Messaging.MessageReceivedEventArgs
    System.Windows.Navigation.FragmentNavigationEventArgs
    System.Windows.Navigation.NavigationEventArgs
    System.Windows.Navigation.NavigationFailedEventArgs
    System.Windows.Printing.BeginPrintEventArgs
    System.Windows.Printing.EndPrintEventArgs
    System.Windows.Printing.PrintPageEventArgs
    System.Windows.RoutedEventArgs
    System.Windows.StartupEventArgs
    System.Windows.VisualStateChangedEventArgs
    System.Xml.Linq.XObjectChangeEventArgs

 

 

이상 강좌를 마치도록 하겠습니다

즐거운 한 주 되세요~~

 

내일은 아르헨티나 전 경기가 있네요. 이기면.. 난리 나겠죠 ㅎㅎ. 이기길 기원합니다

이름
비밀번호
홈페이지
ZN <- 왼쪽의 문자를 오른쪽 박스에 똑같이 입력해 주세요