C# Delegate - Event

기존 코드

비디오 인코드를 끝내고 완료 이메일을 보내라. 라는건데…이렇게 되잇음.

public class Video
{
    public string Title { get; set; }
}

public class Program
{
    static void Main(string[] args)
    {
        var video = new Video() { Title = "Video 1 " };
        var videoEncoder = new VideoEncoder(); //publisher
        var mailService = new MailService(); //subscriper
        videoEncoder.Encode(video);
        //만약에 여기에 5분 기다리라라는 코드가 있으면???
        mailService.SendEmail();
    }
}

public class MailService
{
    public void SendEmail()
    {
        Console.WriteLine("mail service: sending an email ...");
    }
}

public class VideoEncoder
{
    public void Encode(Video video)
    {
        Console.WriteLine("Encoding Video...");
        Thread.Sleep(3000);
    }

}

확장성에 많은 문제가 있다.

이메일을 보내는 코드는 videoEncoder에 잇어야할듯 싶다. 그리고 이벤트 ( 뭔가가가 끝났다..)를 이용한 코드로 수정해보자.

Event는 클래스 내에서 특정한 일이 일어났음을 외부의 event 가입자(subscriper)에게 알려주는 기능을 한다. c#에서는 event라는 키워드로 표시한다. 클래스 내에서는 필드처럼 정의된다. 아래 샘플

public event VideoEncodedEventHandler VideoEncoded; //필드처럼 사용된다.

이벤트를 사용하는 코드로 수정

수정해보자.

public class Program
    {
        static void Main(string[] args)
        {
            var video = new Video() { Title = "Video 1 " };
            var videoEncoder = new VideoEncoder(); //publisher
            var mailService = new MailService(); //subscriper

            videoEncoder.VideoEncoded += mailService.OnVideoEncoded;   //OnVideoEncoded() 함수가  아님..
            videoEncoder.Encode(video);
        }
    }

    public class MailService
    {
        public void OnVideoEncoded(object source, EventArgs e)
        {
            Console.WriteLine("mail service: sending an email ...");
        }
    }

    public class VideoEncoder
    {
        // 1 - define a delegate
        // 2 - define an event based on that delegate
        // 3 - Raise the event

        public delegate void VideoEncodedEventHandler(object source, EventArgs args);
        //리턴타입 (void)가 중요하고 (파라미터) 가 중요 . 메소드 시그니쳐라고 함 

        public event VideoEncodedEventHandler VideoEncoded

        public void Encode(Video video)
        {
            Console.WriteLine("Encoding Video...");
            Thread.Sleep(3000);

            OnVideoEncoded();

        }

        protected virtual void OnVideoEncoded()
        {
            if (VideoEncoded != null)
                VideoEncoded(this, EventArgs.Empty); //EventArgs중에서 아무것도 없는 기본값 리턴..
        }
    }

    public class Video
    {
        public string Title { get; set; }
    }

VideoEncoder 에서는 다음이 중요하다.

1 - define a delegate ( delegate를 선언한다. )

2 - define an event based on that delegate ( 델리게이트를 기본으로 한 이벤트를 선언한다.)

3 - Raise the event (이벤트를 발생시킨다. )

위코드를 한번 더 설명하면 1 - public delegate void VideoEncodedEventHandler(object source, EventArgs args); //델리게이트 선언

2 - public event VideoEncodedEventHandler VideoEncoded; // 델리게이트를 이용해서 이벤트 선언

    • protected virtual void OnVideoEncoded() // 이벤트 발생

이렇게 구현을 해두고 난후 나중에 이메일이 아니라 문자로 알림을 받고 싶은경우

단단하게 다음 클래스를 추가하고 메인 함수만 수정하면된다.

public class SMSService
{
    public void OnVideoEncoded(object source, EventArgs e) //method signiture만 맞춰주자. 
    {
        Console.WriteLine("SMS service : send text");
    }
}

메인에서는

public class Program
{
    static void Main(string[] args)
    {
        var video = new Video() { Title = "Video 1 " };

        var videoEncoder = new VideoEncoder(); //publisher

        var mailService = new MailService(); //subscriper
        var smsService = new SMSService();  // subscriper 추가 

        videoEncoder.VideoEncoded += mailService.OnVideoEncoded;   //OnVideoEncoded() 함수가  아님..
        videoEncoder.VideoEncoded += smsService.OnVideoEncoded;   // 추가 
        videoEncoder.Encode(video);
    }
}

두줄 추가하면 된다..

중요한것은 기존에 만들어진 클래스의 수정이 없이 단지 이벤트를 받을 클래스만 만들어서 추가하면되므로 수정이 용의한 코드가 되엇다.

Custom EventArgs 를 사용해보자.

public class VideoEventArgs : EventArgs // 0. 상속을 이용하여 클래스를 만들자. 
{
    public Video Video { get; set; }
}

public class VideoEncoder
{
    public delegate void VideoEncodedEventHandler(object source, VideoEventArgs args); // 1. VideoEventArgs 를 사용하게 변경
    public event VideoEncodedEventHandler VideoEncoded;
    public void Encode(Video video)
    {
        Console.WriteLine("Encoding Video...");
        Thread.Sleep(3000);
        OnVideoEncoded(video); // 4. VideoEventArgs 를 사용하게 변경 
    }
    protected virtual void OnVideoEncoded(Video video) // 3. VideoEventArgs 를 사용하게 변경 
    {
        if(VideoEncoded != null)
            VideoEncoded(this,new VideoEventArgs() {Video = video}); // 2. VideoEventArgs 를 사용하게 변경
    }
}

public class MailService
{
    public void OnVideoEncoded(object source, VideoEventArgs e) // 5. 파라미터 수정 
    {
        Console.WriteLine("mail service: sending an email ..." + e.Video.Title); 
                                    // 6. eventArgs에서 video정보를 사용할수 있다. 
    }
}

복잡해 보이지만 별거 없다. 이걸 하는 이유는 mailservice에서 이벤트에 대한 일을 할때 구체적인 이벤트 정보를 사용하기 위함이다.

조금더 편하게

기존 코드에서

public delegate void VideoEncodedEventHandler(object source, EventArgs args);
public event VideoEncodedEventHandler VideoEncoded

이름을 같이해서 항상 델리게이트를 만들어서 붙여야 하므로 불편하다.

닷넷에서 매번 델리게이트를 만드는 불편함을 없애기 위해서 기본 델리게이트를 제공한다.

EventHandler EventHandler

두개의 델리게이트를 제공한다. 이걸 이용해서 해보자. 해보자

public class VideoEncoder
{
    //커스텀 델리게이트를 삭제한다. 
    //public delegate void VideoEncodedEventHandler(object source, VideoEventArgs args);
    public event EventHandler<VideoEventArgs> VideoEncoded; 
    //EventHandler 델리케이트를 이용하여 이벤트를 만든다. 추가 데이터를 이용하기 위해 제너릭으로 
    //VideoEventArgs 를 넘겻다.

    public void Encode(Video video)
    {
        Console.WriteLine("Encoding Video...");
        Thread.Sleep(3000);
        OnVideoEncoded(video);
    }

}
//나머지는 다 똑같다.

된다..

앞으로 프로그램을 만들때 어떤 이벤트가 일어날때는 이런식으로 처리해야겟다.

중요한것은 기능을 추가할때 기존 코드를 최대한 적게 고칠수 잇게 만들어 줘야한다.

teamsmiley's profile image

teamsmiley

2016-12-27 00:00

Read more posts by this author