숏컷 코드
public class Player
{
public string Name { get; private set; } // 읽기 공개, 쓰기 내부
private int _score; // 클래스 내부만
protected int Level; // 파생 클래스까지
internal string Region { get; set; } // 같은 어셈블리
}문법
어떤 접근 제한자를 먼저 고르면 되나
| 상황 | 먼저 떠올릴 것 |
|---|---|
| 외부 API로 열어야 함 | public |
| 클래스 내부 구현 | private |
| 파생 클래스에만 열어야 함 | protected |
| 같은 어셈블리 내부만 | internal |
| 어셈블리 내부 또는 파생 클래스 | protected internal |
6가지 접근 제한자와 경계
| 제한자 | 같은 클래스 | 파생 클래스 (동일 어셈블리) | 파생 클래스 (외부 어셈블리) | 동일 어셈블리 | 외부 어셈블리 |
|---|---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ | ✅ |
protected internal | ✅ | ✅ | ✅ | ✅ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ | ❌ |
internal | ✅ | ✅ | ❌ | ✅ | ❌ |
private protected | ✅ | ✅ | ❌ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ | ❌ |
기본 접근 제한자
명시하지 않으면 컴파일러가 기본값을 적용합니다.
class Foo { } // internal (네임스페이스 수준 타입)
class Bar
{
int x; // private (멤버)
void M() { } // private (메서드)
}public과 internal은 네임스페이스 수준 타입(클래스, 인터페이스 등)에만 사용할 수 있습니다. 멤버 기본값은 private이므로 의도적으로 노출해야 할 것만 명시하는 편이 좋습니다.
internal — 어셈블리 경계
같은 어셈블리(.dll 또는 .exe) 안에서만 접근 가능합니다. 라이브러리 내부 구현을 외부에 숨기면서 라이브러리 안에서는 자유롭게 쓸 수 있는 패턴에 자주 씁니다.
InternalsVisibleTo 어트리뷰트를 쓰면 특정 테스트 어셈블리에만 internal을 열어 줄 수 있습니다.
// AssemblyInfo.cs
[assembly: InternalsVisibleTo("MyLibrary.Tests")]protected internal vs private protected
public class Base
{
protected internal void A() { } // 동일 어셈블리 전체 OR 파생 클래스
private protected void B() { } // 동일 어셈블리의 파생 클래스만
}protected internal은 "어셈블리 내부이거나 OR 파생 클래스"라서 범위가 넓습니다. private protected는 둘 다 충족해야 해서 더 좁습니다.
// ❌ 테스트 편의를 이유로 무조건 public으로 열어 두기
public class OrderCache
{
public void ResetCoreState() { }
}
// ✅ 외부 계약이 아니라면 더 좁은 범위부터 시작
public class OrderCache
{
internal void ResetCoreState() { }
}체크포인트
| 제한자 | 한 줄 설명 |
|---|---|
public | 제한 없음 |
private | 선언된 타입 안에서만 |
protected | 선언 타입 + 모든 파생 타입 |
internal | 같은 어셈블리 안에서 |
protected internal | 같은 어셈블리 OR 파생 타입 |
private protected | 같은 어셈블리의 파생 타입만 |
주의할 점
처음부터 모두 public으로 열어두면 나중에 구조를 바꿀 때 외부 의존성이 걸려 있어 수정이 어려워집니다. 필요한 것만 열고, 나머지는 닫는 것이 기본 원칙입니다.
private set이나 init은 프로퍼티 접근자에 별도의 제한자를 붙이는 패턴으로, 읽기는 공개하고 쓰기만 제한할 때 씁니다. 이 경우 프로퍼티 자체의 접근 제한자보다 더 좁은 범위를 지정해야 합니다.
참고 링크
2 sources