Quick Reference
property는 field를 노출하는 읽기·쓰기 API입니다. get/set은 일반 읽기·쓰기, init은 object initializer 또는 constructor 초기화 중 쓰기, required는 object creation에서 member를 지정하라는 compiler 규칙입니다.
public sealed class Player
{
public required string Name { get; init; }
public int Score { get; private set; }
public bool IsVeteran => Score >= 1_000;
public void AddScore(int amount) => Score += amount;
}
var player = new Player { Name = "Mina" };
player.AddScore(1_000);{ get; set; }는 외부 읽기·쓰기,{ get; private set; }는 type 내부에서만 쓰기입니다.{ get; }는 constructor·initializer에서 값이 정해지는 읽기 전용 property입니다.init과required는 서로 다른 규칙이며 어느 쪽도 nested object를 깊게 불변으로 만들지 않습니다.- DB 조회·network I/O·무거운 계산은 property가 아니라 method로 만듭니다.
접근자와 초기화 시점
| 선언 | 쓸 수 있는 곳 | 맞는 상태 |
|---|---|---|
{ get; set; } | 접근 가능한 모든 caller | 외부 변경을 허용하는 값 |
{ get; private set; } | type 내부 | 외부에는 읽기만 공개하는 상태 |
{ get; } | field/property initializer, constructor | 생성 뒤 바뀌지 않는 값 |
{ get; init; } | initializer, constructor, with 초기화 | 생성 단계에서만 정할 값 |
required ... { get; init; } | object creation에서 지정 필요 | caller가 반드시 제공해야 할 input |
public sealed class ConnectionOptions
{
public required string Host { get; init; }
public int Port { get; init; } = 443;
public string Scheme { get; } = "https";
}
var options = new ConnectionOptions { Host = "api.example.com" };field/property initializer는 constructor 밖에서도 선언할 수 있고, object initializer는 접근 가능한 set/init accessor를 호출합니다. get만 있는 auto-property는 object initializer로 넣을 수 없습니다.
Validation과 required contract
set accessor는 값이 바뀔 때 invariant를 지키는 지점입니다. 실패 방식과 허용 범위를 API 계약으로 정합니다.
public sealed class Health
{
private int _current;
public int Current
{
get => _current;
set => _current = value is >= 0 and <= 100
? value
: throw new ArgumentOutOfRangeException(nameof(value));
}
}required는 compiler가 object creation expression을 분석할 때 확인하는 규칙입니다. reflection, deserializer, older caller, nullable-oblivious code가 runtime에 값을 채워 준다는 보장은 아닙니다. runtime invariant가 중요하면 constructor 또는 validation method도 둡니다.
constructor가 required member를 모두 채운다고 compiler에게 알릴 때만 [SetsRequiredMembers]를 붙입니다. 이 attribute는 검증을 실행하지 않고 caller warning을 신뢰로 바꾸므로, 실제로 모든 required member를 설정하지 않는 constructor에 붙이면 잘못된 객체를 만들 수 있습니다.
계산 property와 API 경계
public sealed class Rectangle(double width, double height)
{
public double Width { get; } = width;
public double Height { get; } = height;
public double Area => Width * Height;
}Area처럼 cheap하고 side-effect 없는 파생 값은 계산 property가 자연스럽습니다. 파일 읽기, cache miss, network 호출, 예외가 흔한 검증처럼 비용·실패가 중요한 작업은 Load..., Try..., Calculate... method로 드러냅니다.
public field를 property로 바꾸는 것은 source call syntax가 비슷해도 binary compatibility, reflection, serialization contract에 영향을 줄 수 있습니다. 공개 API는 처음부터 field/property 선택과 access policy를 명확히 합니다.
자주 틀리는 부분
init property가 가진 List<T>나 array는 초기화 뒤에도 그 collection 내용을 바꿀 수 있습니다. immutable snapshot이 필요하면 immutable collection 또는 defensive copy가 필요합니다.
setter 안에서 network·DB·다른 object의 복잡한 mutation을 숨기면 단순 대입처럼 보이는 caller가 실패와 비용을 예측하기 어렵습니다. 그런 동작은 이름 있는 method로 분리하세요.
참고 링크
2 sources