readonly 키워드
readonly 키워드 (C# 참조)
readonly 키워드는 C#에서 수식자(modifier)로 다섯 가지 맥락에서 쓰여요. 어떤 자리에 붙느냐에 따라 하는 일이 조금씩 달라지는데, 하나씩 살펴볼게요.
-
필드 선언에서
readonly는 필드를 선언 시점이나 같은 클래스의 생성자 안에서만 할당할 수 있다는 뜻이에요. 필드 선언과 생성자 안에서는 readonly 필드에 값을 여러 번 할당하고 다시 할당해도 괜찮아요.그런데 생성자가 끝난 뒤에는 readonly 필드에 값을 할당할 수 없어요. 이 규칙이 값 형식과 참조 형식에서 다르게 작동해요.
- 값 형식은 데이터를 직접 담고 있기 때문에, readonly 값 형식 필드는 불변(immutable)이에요.
- 참조 형식은 데이터에 대한 참조를 담고 있기 때문에, readonly 참조 형식 필드는 항상 같은 객체를 가리켜야 해요. 다만 그 객체 자체는 불변이 아닐 수 있어요.
readonly수식자가 막는 건 필드 값을 다른 인스턴스로 바꾸는 일뿐이에요. 즉 readonly 필드를 통해서 객체 안의 데이터가 바뀌는 것까지는 막지 못해요.
[!WARNING] 가변(mutable) 참조 형식인 외부에 보이는 readonly 필드를 담고 있는, 외부에 노출되는 형식은 보안 취약점이 될 수 있고 경고 CA2104 "Do not declare read only mutable reference types."를 일으킬 수 있어요.
-
readonly struct형식 정의에서readonly는 구조체 형식이 불변이라는 뜻이에요. 자세한 내용은 구조체 형식 문서의readonlystruct 섹션을 참고하세요. -
구조체 형식 안의 인스턴스 멤버 선언에서
readonly는 해당 인스턴스 멤버가 구조체의 상태를 바꾸지 않는다는 뜻이에요. 자세한 내용은 구조체 형식 문서의readonly인스턴스 멤버 섹션을 참고하세요. -
ref readonly메서드 반환에서readonly수식자는 메서드가 참조를 반환하는데 그 참조에 대해 쓰기(write)는 허용되지 않는다는 뜻이에요.- 메서드에
ref readonly매개변수를 선언할 때도 쓰여요.
- 메서드에
이 키워드는 C# 초기 버전부터 지원해요.
readonly 필드 예제
이 예제에서는 클래스 생성자가 year 필드에 값을 할당하긴 하지만, ChangeYear 메서드에서는 year 필드의 값을 바꿀 수 없어요.
:::code language="csharp" source="snippets/ReadonlyKeywordExamples.cs" id="ReadonlyField":::
readonly 필드에 값을 할당할 수 있는 곳은 다음 맥락뿐이에요.
-
선언할 때 변수를 초기화하는 경우, 예를 들어:
public readonly int y = 5; -
인스턴스 필드 선언이 있는 클래스의 인스턴스 생성자 안.
-
정적 필드 선언이 있는 클래스의 정적 생성자 안.
이 생성자 맥락들은 readonly 필드를 out 또는 ref 매개변수로 전달해도 되는 유일한 곳이기도 해요.
[!NOTE]
readonly키워드는 const 키워드와 달라요.const필드는 필드 선언 시점에만 초기화할 수 있는 반면,readonly필드는 필드 선언과 어떤 생성자에서든 여러 번 할당할 수 있어요. 그래서 readonly 필드는 어떤 생성자를 쓰느냐에 따라 서로 다른 값을 가질 수 있어요. 또const필드가 컴파일 타임 상수라면, readonly 필드는 다음 예제처럼 런타임 상수로 쓸 수 있어요.public static readonly uint timeStamp = (uint)DateTime.Now.Ticks;
:::code language="csharp" source="snippets/ReadonlyKeywordExamples.cs" id="InitReadonlyField":::
앞의 예제에서 다음 예제 같은 문장을 쓰면:
p2.y = 66; // Error
다음과 같은 컴파일러 오류 메시지를 받아요.
A readonly field cannot be assigned to (except in a constructor or a variable initializer)
readonly 인스턴스 멤버
readonly 수식자를 써서 인스턴스 멤버가 구조체의 상태를 바꾸지 않는다고 선언할 수 있어요.
:::code language="csharp" source="../builtin-types/snippets/shared/StructType.cs" id="SnippetReadonlyMethod":::
[!NOTE] 읽기/쓰기 프로퍼티라면
get접근자에readonly수식자를 붙일 수 있어요. 일부get접근자는 단순히 private 필드 값을 돌려주는 대신 계산을 하고 그 결과를 캐시하기도 해요.get접근자에readonly수식자를 추가하면, 어떤 결과를 캐시하더라도get접근자가 객체의 내부 상태를 바꾸지 않는다는 걸 보장할 수 있어요.
더 많은 예제는 구조체 형식 문서의 readonly 인스턴스 멤버 섹션을 참고하세요.
ref readonly 반환 예제
ref return에 붙은 readonly 수식자는 반환된 참조를 수정할 수 없다는 뜻이에요. 다음 예제는 원점(origin)에 대한 참조를 반환해요. 호출자가 원점을 수정할 수 없다는 걸 나타내기 위해 readonly 수식자를 사용해요.
:::code language="csharp" source="snippets/ReadonlyKeywordExamples.cs" id="ReadonlyReturn":::
반환되는 형식이 readonly struct일 필요는 없어요. ref로 반환할 수 있는 형식은 무엇이든 ref readonly로도 반환할 수 있어요.
readonly ref readonly 반환 예제
ref readonly return을 구조체 형식의 readonly 인스턴스 멤버와 함께 쓸 수도 있어요.
:::code language="csharp" source="./snippets/ReadonlyKeywordExamples.cs" id="SnippetReadonlyRefReadonly":::
이 메서드는 본질적으로 readonly 참조를, (이 경우 메서드인) 인스턴스 멤버가 readonly라는 사실, 즉 어떤 인스턴스 필드도 수정할 수 없다는 사실과 함께 반환해요.
C# 언어 사양
자세한 내용은 C# 언어 사양을 참고하세요.