sealed 한정자

sealed 한정자 (C# 참조)

클래스를 설계하다 보면 "이 클래스는 더 이상 상속받지 못하게 막고 싶어" 하는 경우가 있어요. 그럴 때 쓰는 게 바로 sealed 한정자예요. 한 클래스에 sealed를 붙이면, 다른 클래스가 그 클래스를 상속받는 일 자체가 막혀요. 아래 예시를 볼게요. 클래스 B는 클래스 A를 상속받았지만, 정작 아무 클래스도 클래스 B를 상속받을 수 없어요.

class A {}
sealed class B : A {}

sealed는 메서드나 속성에도 쓸 수 있어요. 다만 그 대상은 기본 클래스의 virtual 메서드나 속성을 재정의(override)하는 메서드·속성이어야 해요. 이렇게 하면 다른 개발자가 여러분의 클래스에서 파생 클래스를 만들 수는 있지만, 특정 virtual 메서드나 속성만큼은 재정의하지 못하게 막을 수 있어요.

아래 예시를 보면, ZY를 상속받지만, X에 선언되어 Y에서 sealed로 봉인된 virtual 함수 F는 재정의할 수 없어요.

(원문의 해당 예제는 csrefKeywordsModifiers.cs 파일의 id=16 스니펫을 참조해요.)

클래스 안에 새 메서드나 속성을 정의할 때, 아예 상속받는 클래스가 재정의하지 못하게 하려면 virtual로 선언하지 않으면 돼요. 반대로 기본 형식에 선언된 virtual 멤버를 재정의하면서, 그다음 파생 형식이 다시 재정의하지 못하게 막고 싶을 땐 sealed 키워드를 쓰면 되고요. 아래 예시가 바로 그 모습이에요.

public sealed override string ToString() => Value;

sealed 클래스에 abstract 한정자를 쓰면 오류가 나요. abstract 클래스는 추상 메서드나 속성의 구현을 제공하는 클래스가 반드시 상속받아야 하니까, sealed로 상속 자체를 막아버리면 모순이 생기거든요.

sealed를 메서드나 속성에 적용할 때는 항상 override와 함께 써야 해요. 그리고 struct는 본질적으로 sealed라서, 사실상 상속받을 수가 없어요. 이 점도 함께 알아두면 좋아요.

어떤 클래스·메서드·속성을 sealed로 봉인할지 결정할 때는 보통 두 가지를 함께 고려해요.

  • 파생 클래스가 여러분의 클래스를 커스터마이징하면서 얻을 수 있는 잠재적인 이점
  • 파생 클래스가 여러분의 클래스를 수정하다가, 그 클래스가 더는 올바르게 혹은 예상대로 동작하지 않게 될 가능성

정리하면, "확장으로 얻는 이득"과 "잘못 바뀔 위험" 사이에서 저울질을 해보는 거예요. sealed는 그 위험 쪽이 더 크다고 판단될 때 쓰는 도구예요.

그런데 sealed 클래스를 상속하려고 시도하면 어떻게 될까요? 아래 같은 코드를 써봤다고 해볼게요.

class MyDerivedC: SealedClass {}   // Error

그럼 이렇게 오류 메시지가 나와요.

'MyDerivedC': cannot derive from sealed type 'SealedClass'

더 알아보기

출처: sealed modifier - C# Reference