7. 이벤트
1. 대리자 vs 이벤트 선언 방법
: 이벤트는 대리자를 event 키워드로 수식해서 만든다.
A.
이벤트는 대리자와 같은 기반을 가진 개념으로, 대리자를 이해한 후 이벤트를 학습해야 한다.
B.
이벤트가 대리자와 다른점은, 이벤트는 메서드 안에서만 호출해서 사용 가능하다는 점이다.
// 대리자 선언
delegate void Callback(string message);
class Button
{
public Callback? MyHandler; // 대리자 필드
public event Callback? Click_Signal; // 이벤트로 변경 (외부에서 Invoke 금지)
public void RaiseClick(string message)
{
Click_Signal?.Invoke(message);
}
}
class MainApp
{
static void Main(string[] args)
{
var btn = new Button();
// 대리자는. 외부에서도 직접 호출 가능 (안전하지 않을 수 있음)
btn.MyHandler?.Invoke("대리자 직접 호출 예시");
// 이벤트는, 외부에서 직접 호출 ❌
// btn.Click_Signal?.Invoke("이벤트 직접 호출 예시"); // 컴파일 에러
// 내부 메서드를 통해서만 실행 가능
btn.RaiseClick("You've got mail.");
}
}
- 대리자 = "메서드 참조를 저장/실행할 수 있는 변수"
- 이벤트 = "대리자를 이용하지만, 발행자 내부에서만 실행할 수 있는 안전한 신호 장치"
2. 예시
A.
대리자를 통해 콜백 개념을 익혔다면,
이벤트가 시작되는 로직과, 대리자로 연결하는 부분, 신호를 전달받아 동작하는 로직을 구분할 수 있다.
B. 가정
- 아래 예제는, 버튼이 눌려지면, 레이블에 문자열이 수정되는 구조를 이벤트로 구현한다고 가정하자.
eg) [버튼 클릭 > 이벤트(대리자)] > 레이블 수정 - 예제 구조에서 구현된 이벤트는, 이벤트를 선언하는 목적이 아니다.
레이블의 수정이 목적이다.- [1] 시작은 Button 객체의 click 함수의 실행
- [2] 이벤트가 실행되면
- [3] 목적은 Label 객체의 show 함수의 실행이다.
- 이벤트는 click 함수가 show 함수를 실행하게 만드는 대리자(콜백)이다.
이벤트는 최종 실행 목적인 show 함수를 기반으로 선언된다.

delegate void callback(string msg);
class Button
{
public event callback EventHandler;
public void click(string x) // 2.1 함수의
{
EventHandler(x); // 2.2 이벤트 동작
// btn.click() -> btn.EventHandler() -> lbl.show() 순으로 함수가 호출된다.
// 이벤트가 동작된 후 최종 실행 구조는 아래와 같다.
//lbl.show("click~!!")
}
}
class Label
{
public void show(string x) // 4.1 함수의
{
Console.WriteLine("Lable print >> " + x); // 4.2 매개변수 전달받아 동작
}
}
internal class Program
{
static void Main(string[] args)
{
Button btn = new Button();
Label lbl = new Label();
// 버튼 객체의 이벤트(=대리자)에, 레이블 객체의 함수를 등록한다.
btn.EventHandler += new callback(lbl.show); // 3. 이벤트에 연결된 lbl.show() 동작
btn.click("click~!!"); // 1. 버튼의 click 함수가 실행되면
}
}
3. 이벤트 실행 흐름 이해
: 소스코드는 한 줄 씩 실행된다. 실행 흐름을 이해해야 한다.
A. 예제1
// 버튼을 누르면, 레이블에 출력된다고 가정한다.
using System;
namespace EventTest
{
delegate void Callback(string message);
class Button
{
public event Callback CollbackEvent;
public void Check369(int number) // [2]
{
int temp = number % 10;
if (temp != 0 && temp % 3 == 0)
CollbackEvent(String.Format("{0} : 짝", number)); // [3]
}
}
class Label
{
public void ShowMsg(string message) // [5]
{
Console.WriteLine(message); // [6]
}
}
class MainApp
{
static void Main(string[] args)
{
Button btn = new Button();
Label lbl = new Label();
// 버튼 객체의 이벤트(=대리자)에, 레이블 객체의 함수를 등록한다.
// 버튼 객체의 이벤트(=대리자)에, 레이블 객체의 함수
btn.CollbackEvent += new Callback(lbl.ShowMsg); // [4]
// 버튼이 눌려진다고 가정한다.
// 버튼이 눌려질 때마다 정수 1~30을 순차적으로 전달한다.
// 1. 버튼이 눌려지고 -> 2. Check369() 함수가 실행되고
// 3. CollbackEvent가 실행되고, 매개변수로 "문자열"이 전달된다.
// 4. CollbackEvent에 연결된, 레이블의 ShowMsg함수가 실행된다.
for (int i = 1; i < 30; i++)
btn.Check369(i); // [1]
}
}
}
B. 예제2
// 이것이 c#이다. 교재 496쪽 소스코드
using System;
namespace EventTest
{
delegate void EventHandler(string message);
class MyNotifier
{
public event EventHandler SomethingHappened;
public void DoSomething(int number)
{
int temp = number % 10;
if ( temp != 0 && temp % 3 == 0)
{
SomethingHappened(String.Format("{0} : 짝", number));
}
}
}
class MainApp
{
static public void MyHandler(string message)
{
Console.WriteLine(message);
}
static void Main(string[] args)
{
MyNotifier notifier = new MyNotifier();
notifier.SomethingHappened += new EventHandler(MyHandler);
for (int i = 1; i < 30; i++)
{
notifier.DoSomething(i);
}
}
}
}
예제 3
카운팅이라는 로직을 가진 객체의 이벤트가 동작할때마다, 텍스트박스 객체에 출력된다고 가정한다.
using System;
namespace delegate_test4
{
delegate void A (string sender);
class TextBox
{
private string title;
public TextBox(string name)
{
this.title = name;
}
public void run(string run_msg)
{
Console.WriteLine( run_msg + title + "에서 출력!");
}
}
class Program
{
static void Main(string[] args)
{
TextBox TB1 = new TextBox("텍스트창1");
TextBox TB2 = new TextBox("텍스트창2");
TextBox TB3 = new TextBox("텍스트창3");
TextBox TB4 = new TextBox("텍스트창4");
Counting cnt = new Counting();
cnt.Cnt_EventHandler += TB1.run;
cnt.Cnt_EventHandler += TB2.run;
cnt.Cnt_EventHandler += TB3.run;
cnt.Cnt_EventHandler += TB4.run;
cnt.nCnt();
}
}
class Counting
{
public event A Cnt_EventHandler;
public void nCnt()
{
for (int i = 0; i < 10; i++)
{
Cnt_EventHandler($"이벤트, {i+1}회 실행 >> ");
}
}
}
}

4. 윈폼 예제
A. 구조 이해
button1 이라는 객체에, button1.Click 이벤트에, Form1의 button1_click( ) 함수가 등록되었다.
"Button 객체의 Event" 라는 대리자가 실행되면, 윈폼의 함수, 윔폼의 컨트롤, 또는 특정 실행 로직을 가진 함수가 실행되도록 구현하는 개념이다.


5. 윈폼 파일 복사 (동기 구조)
해당 로직은 동기화 방식으로, 파일 복사 실행중에는 UI가 멈춤
event_test 프로젝트 경로에 "파일 복사"에 사용 할 파일 src.mp4 파일이 있어야 합니다.
(TCP/IP 서버-클라이언트 구조에서 파일 전송으로 가정하고 접근합니다.)
이벤트 제공자 : button1.click
이벤트 가입자 : FileManager
환경: Visual Studio 2022, net8.0

해당 로직은 동기화 방식으로, 파일 복사 실행중에는 UI가 멈춤
6. 윈폼 파일 복사 (비동기)
event_test 프로젝트 경로에 "파일 복사"에 사용 할 파일 src.mp4 파일이 있어야 합니다.
이벤트 제공자 : FileManager
이벤트 가입자 : Form1
환경: Visual Studio 2022, net8.0
