2024.03.11 - 이벤트(event)
어떤 조건을 달성하거나 일이 생겼을 때 이것을 알려주는 객체를 이벤트라고 한다.
이벤트는 대리자(delegate)를 event 한정자로 수식해서 만들 수 있다.
이벤트는 클래스 내부에 위치해야 한다.
- 이벤트를 선언하고 사용하는 방법
- 대리자를 선언한다.
- 클래스 내부에서 선언한 대리자의 인스턴스를 event 한정자로 수식해서 선언한다.
- 이벤트 핸들러를 작성한다. 이벤트 핸들러는 1에서 선언한 대리자와 반환형식/매개변수가 일치하는 메소드이다.
- 이벤트가 들어있는 클래스의 인스턴스를 생성하고, 이 객체의 이벤트에 3에서 작성한 이벤트 핸들러를 등록한다.
- 클래스 내부의 동작으로 이벤트가 발생하면, 이벤트 핸들러가 호출된다.
이벤트가 들어있는 클래스? 이벤트 핸들러? 말만 들어서는 뭔소리를 하는지 모르겠다.
이벤트를 코드로 만들어보면서 이해해보자.
1. 대리자를 선언한다.
EventHandler라는 이름의 대리자를 선언했다. 여기까진 그냥 대리자 만드는 거랑 똑같다.
2. 클래스 내부에서 선언한 대리자의 인스턴스를 event 한정자로 수식해서 선언한다.
클래스(TestEvent) 내부에서 대리자(EventHandler)의 인스턴스(SomthigHappend)를 event 한정자로 수식해서 선언했다. 뭔진 몰라도 이벤트는 클래스 내부에서만 동작한다. 그래서 클래스 안에서 이벤트(event로 한정된 대리자)를 만들어 주었다.
3. 이벤트 핸들러를 작성한다. 이벤트 핸들러는 1에서 선언한 대리자와 반환형식/매개변수가 일치하는 메소드이다.
이벤트 핸들러는 나중에 이벤트를 인스턴스를 생성할 때 인수로 넣을, 이벤트가 호출할 메소드라고 생각하면 된다.
4. 이벤트가 들어있는 클래스의 인스턴스를 생성하고, 이 객체의 이벤트에 3에서 작성한 이벤트 핸들러를 등록한다.
5. 클래스 내부의 동작으로 이벤트가 발생하면, 이벤트 핸들러가 호출된다.
흠.. 아직 잘 모르겠지만, 클래스 내부의 동작(OccurEvent)으로
for문 안에서 i가 짝수일 때마다 이벤트(SomethingHappend)가 발생해서,
이벤트 핸들러(MyHandler)가 호출되어서 "이벤트 핸들러가 호출됬음!"이 출력되었다.
- 이벤트 vs 대리자
이벤트를 더 이해하기 쉽게 하기 위해서, 같은 코드를 이벤트 대신 대리자를 통해 작성해보았다.
어? 이렇게 보니까 이벤트는 그냥 대리자랑 똑같은 거 같은데?
그렇다. 이벤트는 대리자인데 event 한정자를 붙인 것일 뿐이다.
이벤트 핸들러는 대리자(이벤트)가 생성시 인수로 받을 메소드라고 생각하면 된다.
단지 이벤트가 클래스 안에서 동작하기 때문에 헷갈리는 것 뿐이다.
이벤트는 어떤 조건이 달성되었을 때 메소드를 호출하고 싶으면 쓰는 대리자..라고 이해하면 될 것 같다.
- 근데 대리자로 이벤트가 하는 거 똑같이 할 수 있으면 이벤트는 도대체 왜 쓰는 것임?
이벤트랑 대리자는 몇가지 차이점이 있다. 간단한 코드 예시로 비교해보자.
- 대입연산자로 이벤트에 호출할 이벤트 핸들러(메소드)를 등록할 수 없다.
이벤트에 이벤트 핸들러를 등록할 때는 += 연산자만 사용할 수 있다.
이것은 불편해 보이지만 아래와 같은 장점을 가진다.
+= 연산자로 대리자 체인을 활용하여 Print를 3번 등록했다. 코드를 실행하면 출력이 3번 됨을 확인할 수 있다.
이 상태에서 대리자에 대입을 통해 Print를 등록하면 의도하지 않은 동작이 일어날 수 있다.
마지막에 대리자에 대입연산자로 Print를 등록했더니 앞에서 등록했던 Print는 다 날아가고 마지막으로 대입한 Print만 남았다. 이벤트는 등록에 +=연산자만 사용할 수 있어 이렇게 등록한 메소드가 의도치 않게 손실될 일이 없다.
- 이벤트는 클래스 외부에서 호출할 수 없다.
이벤트는 객체 외부에서 호출할 수 없다. 이벤트를 객체 외부에서 임의로 호출할 수 있게 되면 객체 내부의 상태를 허위로 바꿀 수 있게 된다. 대리자로는 이런 사고를 미연에 방지할 수 없기 때문에 이벤트를 사용해서 객체 내부 상태를 알리는 것이 좋다.
덤.