[More Effective C#] 2장 - Item 21: 이벤트는 가상으로 선언하지 말라

SY·2023년 10월 24일

More Effective C#

목록 보기
21/46
post-thumbnail

빌 와그너, ⌜More Effective C# 2판⌟, 김완섭 옮김, 한빛미디어, 2019
이 포스팅은 ⌜More Effective C# 2판⌟의 내용을 기반으로 작성되었습니다.


아이템 21: 이벤트는 가상으로 선언하지 말라



C#에서는 다른 클래스 멤버처럼 이벤트도 가상으로 선언할 수 있다.
필드와 정의 방식으로 이벤트를 선언하면 내부적으로 add(), remove()와 같은 함수가 만들어지기 때문에 다른 클래스 가상 선언처럼 문제가 단순하지 않다.
베이스 클래스와 파생 클래스에서 각자 이벤트 핸들러를 만들다 보면 예기치 않은 문제가 발생하곤 한다.



이벤트를 선언하면 컴파일러는 내부적으로 public 메서드로 add(), remove()를 생성한다.
이런 계약은 파생 이벤트에도 적용된다.
컴파일러는 제대로 된 방식으로 이벤트를 재정의한 코드를 생성할 수 있다.
그러면 파생이벤트를 만들면 베이스 클래스에 선언된 이벤트를 감추게 된다.

따라서, 이후 이벤트 핸들러는 모두 파생 클래스에 추가된 이벤트에 연결되어 베이스 클래스의 이벤트 발생은 일어나지 않는다.
베이스 클래스의 코드에서 이벤트를 발생시키더라도 이벤트 핸들러에 전달되지 않는다.



유일한 해결 방법은 파생 클래스에서 add()와 remove() 접근자를 명시적으로 작성하는 방법이 있다.

  • 예시 코드
public class WorkerEngineDerived : WorkerEngineBase
{
	protected override void SomeWork()
    {
    	Thread.Sleep(50);
    }
    public override event EventHandler<WorkerEventArgs> OnProgress
    {
    	add { base.OnProgress += value; }
        remove { base.OnProgress -= value; }
    }
}
  • 위의 예시 코드는 파생 클래스에서 이벤트를 발생시킬 수 없는데, 이를 개선하려면 베이스 클래스를 수정해야 한다.

  • 베이스 클래스에 protected로 저장용 필드를 추가하여, 파생 클래스에서 이 필드의 값을 수정할 수 있도록 해주는 방법이 있다.

  • 하지만 이 경우에는 파생 클래스가 더 이상 필드 정의 방식으로 이벤트를 정의할 수 없다는 제약 사항이 생긴다.



두 가지 해결 방법

  • 가상 이벤트를 만들 때 필드 정의 방식으로 이벤트를 정의하지 않는 방법
  • 가상 이벤트 정의를 생성할 때마다 이벤트를 발생시키는 가상 메서드를 만드는 방법


가상 이벤트를 만들면 이벤트 발신자와 해당 이벤트를 수신하는 클래스 사시에 컴파일타임 결합도와 런타임 결합도가 모두 높아진다.
가상 이벤트를 추가하고, 이것이 제대로 동작하도록 코드를 추가하다 보면, 역설적으로 가상 이벤트가 필요하지 않다는 것을 알 수 있다.

profile
게임 개발 공부

0개의 댓글