SSISO Community

갤러리정

위임과 이벤트(Delegate & Event) - CSharp

위임과 이벤트(Delegate &  Event)
- 메소드의 실행을 위임 변수가 대행
- 메소드 포인터
- 위임형식은 동일한 파라미터와 반환형을 가지는 여러개의 메소드를 수행 가능하다.
- 객체 메소드 or 정적메소드 둘다 가능
- 위임은 메소드 포인터만 저장, 코드 기술은 불가
- 인터페이스를 지원하는 객체들 내에서 동일한 메소드 명으로 객체 내의 메소드 수행이 가능하듯이 위임도 동일한 위임명으로 메소드 형식이 동일한 다른 메소드 실행이 가능(다형성)

 

위임선언
- public delegate 반환형 위임명(인자리스트);
- delegate 키워드(본문X)
- 메소드 파라미터 형식과 메소드 반환형식이 같으면 위임 형식에 위임 가능

 

publid delegate void Sample (int x, int y);
- public static void M1(int x, int y);         //가능
- public void M2(int x, int y)                  //가능
- public int M3(int x, int y);                   //X, 리턴타입이 다름
- public void M4(int x);                        //X, 인자갯수가 다름
- public void M5(string x, int y);          //X, 인자의 타입이 다름
- public void M6(int x, int y, int z);      //X, 인자수가 다름

 

위임생성
- System.Delegate와 System.MulticastDelegate로 부터 상속
- 따라서 new 연산자로 위임 객체 생성
- 생성자의 인자는 반드시 정적메소드면 "클래스명.메소드명", 객체메소드면 "객체명.메소드명"
   
    Sample d = new Sample(Class.M1);     //인자에 메소드명만 들어간다.
    Sample d = new Sample(obj.M2);

 

using System;

namespace CSharpStudy
{
    class MyClass
    {
        public int num = 0;
        public void Plus(int value)
        {
            this.num +=value;
        }
        public void Minus(int value)
        {
            this.num -=value;
        }
        public static void PrintHello(int value)
        {
            for(int i=0; i<value; i++)
                Console.WriteLine("Hello~");
        }
    }

    //Delagete선언
    public delegate void Sample(int value);

    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            MyClass c = new MyClass();
            Sample d =  new Sample(c.Plus);

 

            //위임
            d(10);
            Console.WriteLine(c.num);

            c.Plus(10);
            Console.WriteLine(c.num);

            d = new Sample(c.Minus);
            d(10);
            Console.WriteLine(c.num);

            c.Minus(10);
            Console.WriteLine(c.num);

 

            //정적메소드 위임
            d = new Sample(MyClass.PrintHello);
            d(5);
           
        }
    }
}

 

위임사용
- 위임 객체 사용은 일반메소드와 동일
- 위임 호출시 파라미터를 전달하면 실제 메소드에 파라미터 전달, 호출
- 동일한 위임객체로 어떤 메소드를 참조하느냐에 따라 호출변경(메소드 다형성)
- 위임자는 변경할 수 없으며 일단 만들어지면 위임자의 호출 목록은 바뀌지 않는다.
- 여러 개의 호출목록을 지정해 주려면 그 Delegate의 형식은 리턴 타입이 반드시 void 이어야 함.
        ->  매개변수중에 어느것도 out 이 지정되어선 안된다.


위임연산
- Combine(+), Remove(-)
- 위임 객체에 메소드 추가, 제거
- 위임 객체가 가지는 메소드 리스트(Invocation List)

 

Combine & Remove
- Combine과 Remove를 명시적으로 호출해서 리스트 관리 기능

 

위임간 비교연산
- Equals, ==, != 재정의
- Invocation List를 비교한다.

 

using System;
namespace CSharpStudy
{
    // Mult Delegate 사용에 관한 예제입니다.

    class MyClass
    {
        public int num = 0;
        public void Plus(int value)
        {
            this.num +=value;
        }
        public void Minus(int value)
        {
            this.num -=value;
        }
        public static void PrintHello(int value)
        {
            for(int i=0; i<value; i++)
                Console.WriteLine("Hello~");
        }
    }

    //delegate 선언
    public delegate void Sample(int value);

 

    // 다중 메소드 연산이 가능
    // 멀티 Delegate를 사용할 때 메서드는 반드시 void를 반환해야 함 -> 호출을 목적으로 하기때문
    // void를 반환하지 않으면 +=과 같은 연산자를 사용할수 없다.
    // 중복 메서드를 포함 가능
    //호출된 메서드가 예외를 throw하면 메서드는 실행을 멈추고 예외는 대리자의 호출자에게 전달.
    //호출 목록에 남아있는 메서드는 호출되지 않음. 호출자 안에서 예외를 catch해도 이 동작은 변하지 않습니다.

 

    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            //위임 메소드 리스트 관리
            MyClass c = new MyClass();
            Sample d;

 

            //Delegate Combine
//          d = (Sample)Delegate.Combine(new Sample(c.Plus), new Sample(c.Minus));
            d =  new Sample(c.Plus) + new Sample(c.Minus);      // +연산자 오버로딩됨
            d(10);                  //c.Plus와 c.Minus 메소드가 순서대로 실행됨. 결과값 : 0
            Console.WriteLine(c.num);

 

            //Delegate Remove
//          d = d - new Sample(c.Minus);    //c.Minus메소를 제거
            d -= (Sample) new Sample(c.Minus);          // -=연산자 오버로딩됨
            d(10);                  //c.Plus메소드만 실행. 결과값: 10
            Console.WriteLine(c.num);

 

            //Delegate Combine
            d += new Sample(MyClass.PrintHello);

           //MyClass.PrintHello 메소드가 추가, +=연산자 오버로딩됨
            d(5);   //c.Plus와 MyClass.PrintHello 메소드 실행, 오래등록된 메소드부터 실행
            Console.WriteLine(c.num);

 

            //Deleate 비교연산  -> 가지고 있는 메소드 목록(목록 순서, 목록갯수 포함)을 비교한다.
            Sample s1 = new Sample(c.Plus) + new Sample(c.Minus);
            Sample s2 = new Sample(c.Plus);

 

            if(s1 == s2)
                Console.WriteLine("s1과 s2가 같습니다");
            else
                Console.WriteLine("s1과 s2가 다릅니다");
            // 가지고 있는 목록이 다르므로 다르다는 내용 출력

            s2 += new Sample(c.Minus);

            if(s1 == s2)
                Console.WriteLine("s1과 s2가 같습니다");
            else
                Console.WriteLine("s1과 s2가 다릅니다");
            // 가지고 있는 목록이 같으므로 같다는 내용 출력

      
        }
    }
}

 

이벤트(Event)
- 동작을 수행하는 방식
- 출판자(Publisher) : 이벤트를 발생하며 특정 구독자에게 이벤트 발생을 알려준다.
- 구독자(Subscriber) : 특정 이벤트 발생 통보를 받고 출판자로부터 호출되어질 메소드를 등록한

 

객체
- 이벤트 핸들러(Event Handler) : 호출되어지는 메소드
- 구독자들이 출판자에게 자신의 이벤트 핸들러를 출판자의 이벤트 핸들러 목록에 등록한다.

 

이벤트 정의
- event 키워드
- 접근지정자 event Delegate명 이벤트명;
- 반드시 위임과 같이 정의한다.

 

이벤트 핸들러 추가 및 해제
- +=, -+로 이벤트 핸들러를 출판자 객체에게 등록 및 제거한다.
- 위임 등록과 동일
- 객체명.이벤트명 += new Delegate명(객체명.메소드명);

 

구독자들에게 이벤트 발생 알리기
- 위임에서 위임 객체를 실행하며 Invocation List안에 모든 메소드를 실행하듯이 이벤트 요청이 발생하면 이벤트안에 이벤트 핸들러가 있는지 조사하고 이벤트 핸들러를 호출한다.

 

이벤트 핸들러 생성
- 모든 형태가능 -> 단 위임 형식과 동일한 메소드 형식
- 보통 포준적인 이벤트 핸들러 형식
  public delegate void EventHandler(object sender, EventArgs args)


- object sender : 동일한 이벤트 핸들러가 다중의 출판자에게 등록이 가능하므로 sender를 가지고 호출자(출판자)를 구별
- EventArgs args : 이벤트 발생시 추가 정보 전달
- 모든 이벤트 핸들러는 이 위임형과 반드시 동일

 

using System;

namespace CSharpStudy
{
    class Button
    {
        public event ButtonClick Click; //이벤트생성, 접근지정자 event Delegate명 이벤트명;
        public string Text;

        public void Trigger()
        {
            ButtonClickEventArgs e = new ButtonClickEventArgs();
            e.i = 100;
            Console.WriteLine("Button이 클릭되었습니다");
            Click(this, e); //실제로 이벤트를 발생시키는 부분
        }
    }

    //delegate
    public delegate void ButtonClick(object sender, ButtonClickEventArgs e);

    //사용자 정의 EventArgs
    public class ButtonClickEventArgs : EventArgs
    {
        public int i;
    }

    class  MainClass
    {

        //이벤트 핸들러 선언
        public static void button1_Click(object sender, ButtonClickEventArgs e)
        {
            //object sender는 메시지가 어디서 발생하는지 발생된 곳의 참조값
            // ButtonClickEventArgs e는 이벤트의 데이터를 담고 있는 매개변수.
            //이 두가지의 매개변수는 바꿀 수 없으며 항상 위와 같은 규칙으로 사용해야 함.

            Button button = (Button) sender;
            Console.WriteLine("Button Text : " + button.Text);
            Console.WriteLine("EventArgs : " + e.i);
            Console.WriteLine("ButtonClick 이벤트가 발생해서 이벤트 핸들러가 호출되었습니다.");
        }

        public static void button1_Click2(object sender, ButtonClickEventArgs e)
        {
            Console.WriteLine("Click2~");
        }

        [STAThread]
        static void Main(string[] args)
        {
            Button btn1 = new Button();
            btn1.Text = "테스트 버튼";
            btn1.Click += new ButtonClick(button1_Click); 

            //ButtonClick(위임자)에 호출메서드 등록
            btn1.Click += new ButtonClick(button1_Click2);

            btn1.Trigger();     //이벤트 발생 에뮬레이터

            Console.Read();     //실행화면 일시중지
        }
    }
}


1135 view

4.0 stars