protected
protected (C# 레퍼런스)
protected 키워드는 멤버 접근 한정자(member access modifier) 중 하나예요. 이 한정자를 달면 해당 멤버를 자기 클래스 안에서도, 파생 클래스의 인스턴스에서도 접근할 수 있게 돼요.
본문
먼저 짚고 넘어갈 점이 있어요. 이 글은 protected 접근 자체를 다루는데, protected 키워드는 사실 protected internal과 private protected 한정자의 일부로도 쓰여요. 그래서 혼동하지 않도록, 이번엔 순수한 protected에만 집중할게요.
그럼 핵심 규칙부터 확인해 볼까요. protected 멤버는 기본 클래스의 파생 클래스에서도, 접근이 파생 클래스 타입을 통해서 일어날 때만 접근할 수 있어요. 말로만 하면 잘 안 와닿으니 코드로 보는 게 빨라요.
//<snippet1>
namespace Example1
{
class BaseClass
{
protected int myValue = 123;
}
class DerivedClass : BaseClass
{
static void Main()
{
var baseObject = new BaseClass();
var derivedObject = new DerivedClass();
// Error CS1540, because myValue can only be accessed through
// the derived class type, not through the base class type.
// baseObject.myValue = 10;
// OK, because this class derives from BaseClass.
derivedObject.myValue = 10;
}
}
}
//</snippet1>
여기서 baseObject.myValue = 10은 오류를 내요. baseObject는 BaseClass 타입, 즉 기본 클래스 참조로 protected 멤버에 접근하고 있으니까요. protected 멤버는 파생 클래스 타입, 또는 그 타입에서 더 파생된 타입을 통해서만 접근할 수 있다는 점을 기억하세요.
그럼 private protected나 protected internal과는 어떤 점이 다른지 비교해 볼게요.
private protected와 달리,protected는 어느 어셈블리(assembly)에 있든 파생 클래스에서 접근을 허용해요.protected internal과 달리, 같은 어셈블리 안의 파생되지 않은 클래스에서는 접근을 허용하지 않아요.
그리고 한 가지 재미있는(?) 제약이 있어요. 구조체(struct)는 상속을 받을 수 없기 때문에 protected 멤버를 선언할 수 없어요. 구조체를 쓰다 protected가 안 된다고 에러가 나면, 그 이유가 바로 이거예요.
이번엔 실제로 파생 클래스에서 protected 멤버를 직접 쓰는 예시를 볼게요. DerivedPoint가 Point에서 파생되니까, 기본 클래스의 protected 멤버에 파생 클래스에서 바로 접근할 수 있어요.
//<snippet1>
namespace Example2
{
class Point
{
protected int x;
protected int y;
}
class DerivedPoint: Point
{
static void Main()
{
var dpoint = new DerivedPoint();
// Direct access to protected members.
dpoint.x = 10;
dpoint.y = 15;
Console.WriteLine($"x = {dpoint.x}, y = {dpoint.y}");
}
}
// Output: x = 10, y = 15
}
//</snippet1>
참고로 만약 여기서 x와 y의 접근 수준을 private으로 바꾸면, 컴파일러가 이런 오류 메시지를 돌려줘요.
'Point.y' is inaccessible due to its protection level.
'Point.x' is inaccessible due to its protection level.
마지막으로, protected 멤버가 서로 다른 어셈블리에 있어도 파생 클래스에서 접근 가능하다는 걸 보여주는 예시를 확인해 보죠. 아래는 어셈블리 1과 어셈블리 2로 나뉘어 있는 코드예요.
// Assembly1.cs
// Compile with: /target:library
namespace Assembly1
{
public class BaseClass
{
protected int myValue = 0;
}
}
// Assembly2.cs
// Compile with: /reference:Assembly1.dll
namespace Assembly2
{
using Assembly1;
class DerivedClass : BaseClass
{
void Access()
{
// OK, because protected members are accessible from
// derived classes in any assembly
myValue = 10;
}
}
}
이런 어셈블리 간 접근이 바로 protected가 private protected와 다른 점이에요. private protected는 같은 어셈블리로 접근을 제한하죠. 반면 protected internal과는 비슷하면서도, protected internal은 같은 어셈블리의 파생되지 않은 클래스에서도 접근을 허용한다는 차이가 있어요.
더 알아보기
- C# Keywords
- Access Modifiers
- Accessibility Levels
- Modifiers
- public
- private
- internal
- Security concerns for internal virtual keywords
C# 구문과 사용법에 대한 최종적인 기준은 C# 언어 사양의 선언된 접근성(Declared accessibility) 문서에 있어요. 궁금한 게 더 생기면 그쪽을 참고하면 돼요.