C#에서 readonly 키워드는 멤버 변수(필드)를 상수처럼 취급하면서도, 그 값을 런타임에 계산하거나 할당할 수 있도록 허용하는 한정자입니다. 이는 컴파일 시점에 값을 반드시 확정해야 하는 const 한정자와 가장 큰 차이를 보입니다.
readonly를 사용하면 필드의 값을 선언부에서 직접 초기화하거나, 해당 필드가 속한 클래스의 생성자 안에서 할당할 수 있습니다. 덕분에 설정 파일, 환경 변수, 데이터베이스 조회 결과처럼 실행 시점에만 알 수 있는 값도 불변(immutable) 필드로 안전하게 관리할 수 있습니다.
readonly 한정자가 사용되는 네 가지 맥락
C#에서 readonly 한정자는 다음 네 가지 문맥에서 사용할 수 있습니다.
필드 선언(Field declaration) — 인스턴스 또는 정적 필드를 읽기 전용으로 만듭니다.
읽기 전용 구조체(readonly struct) — 구조체 전체가 불변임을 명시하여, 모든 멤버가 변경 불가능하도록 강제합니다.
읽기 전용 멤버(readonly member) — 구조체 내 특정 메서드나 속성이 상태를 수정하지 않음을 보장합니다.
ref readonly 반환(ref readonly return) — 참조로 반환되는 값이 호출자에 의해 수정되지 않도록 보호합니다.
특히 필드 선언 문맥에서는 다음 규칙을 반드시 기억해야 합니다. 읽기 전용 필드에 값을 할당할 수 있는 시점은 오직 두 곳뿐입니다.
필드를 선언하는 시점
해당 필드가 속한 클래스의 생성자가 호출되는 시점
그 외의 위치에서 값을 수정하려고 하면 컴파일 오류(CS0191)가 발생합니다.
예제 1: 필드 선언에서 readonly 사용하기
class Program
{
readonly string Name;
public Program()
{
Name = "Name"; // 생성자에서 할당 가능
}
static void Main(string[] args)
{
Program a = new Program();
System.Console.WriteLine(a.Name);
Console.ReadLine();
}
}출력 결과
Name
위 예제에서 Name 필드는 생성자 내부에서만 초기화됩니다. 이후 프로그램이 실행되는 동안에는 누구도 이 값을 변경할 수 없습니다.
예제 2: readonly struct 선언하기
C# 7.2부터 도입된 readonly struct는 구조체의 모든 인스턴스 멤버가 불변임을 컴파일러 수준에서 보장합니다. 데이터 무결성이 중요한 값 타입 설계에 매우 유용합니다.
public readonly struct Server
{
public readonly string Name { get; }
public Server(string name)
{
Name = name;
}
}
class Program
{
static void Main(string[] args)
{
Server a = new Server("Domain Controller");
System.Console.WriteLine(a.Name);
Console.ReadLine();
}
}출력 결과
Domain Controller
const와 readonly의 차이 한눈에 보기
| 구분 | const | readonly |
|---|---|---|
| 값 할당 시점 | 컴파일 타임 | 선언부 또는 생성자(런타임) |
| 적용 대상 | 기본 형식, 문자열 등 | 모든 형식(참조 형식 포함) |
| static 여부 | 암묵적으로 static | 인스턴스/정적 모두 가능 |
| 버전 갱신 영향 | 참조 어셈블리 재컴파일 필요 | 런타임에 값 조회 |
마무리
readonly를 적절히 활용하면 코드의 의도를 명확히 드러내고, 실수로 인한 상태 변경을 컴파일 타임에 차단할 수 있습니다. 객체가 생성된 후 변하지 않아야 하는 필드라면 readonly 선언을 습관화하는 것이 좋은 C# 코딩 스타일입니다.