위임과 이벤트(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(); //실행화면 일시중지
}
}
}
SSISO Community