Quick Reference
record는 데이터를 간결하게 선언하고 값 기반 equality를 제공하는 타입 계열입니다. record는 기본적으로 record class이고, record struct는 값 타입입니다. 둘 다 선언만으로 완전 불변이 되지는 않으며 with도 얕은 복사입니다.
public record User(string Name, int Level);
User mina = new("Mina", 5);
User senior = mina with { Level = 6 };
Console.WriteLine(mina == new User("Mina", 5)); // True- positional record class의 생성 프로퍼티는 기본적으로
get; init;입니다. - positional record struct의 생성 프로퍼티는 기본적으로
get; set;입니다. 불변 값이 필요하면readonly record struct를 씁니다. - record class는 참조 타입, record struct는 값 타입이므로 대입·인자 전달의 복사 방식이 다릅니다.
- event payload, 설정 snapshot, DTO처럼 값 비교가 유용한 데이터에 잘 맞습니다. identity와 가변 lifecycle이 핵심이면 class를 고릅니다.
선언 형태와 기본 동작
public record OrderId(string Value); // record class
public record class PlayerProfile(string Name, int Hp);
public record struct Size(int Width, int Height); // 값 타입, 기본 set 가능
public readonly record struct Coordinate(double Lat, double Lon);positional record은 생성자 매개변수에 대응하는 프로퍼티와 deconstruction을 제공합니다. record는 equality, GetHashCode, 사람이 읽기 쉬운 ToString()도 생성하지만, 정확히 어떤 멤버가 equality에 포함되는지는 선언과 inheritance에 따라 달라집니다. 깊은 equality 규칙은 record equality와 with expression에서 다룹니다.
non-positional record는 필요한 property만 직접 선언합니다. required는 생성 시 필수 입력을 compiler에 알리는 것이고, init은 초기화 이후 대입을 막는 setter입니다. 둘 모두 내부 참조 객체를 자동으로 불변으로 만들지는 않습니다.
public record Deployment
{
public required string Region { get; init; }
public required int Replicas { get; init; }
}
var deployment = new Deployment { Region = "ap-northeast", Replicas = 3 };복사와 가변성
with는 원본을 바꾸지 않고 복사본의 지정 member를 바꿉니다. record class에서는 새 인스턴스, record struct에서는 새 값이 만들어집니다. 그러나 nested array, List<T>, 일반 class 같은 참조 멤버는 복사본도 같은 객체를 가리키므로 얕은 복사입니다.
public record BuildOptions(string Name, List<string> Tags);
var original = new BuildOptions("dev", new List<string> { "fast" });
var copy = original with { Name = "prod" };
copy.Tags.Add("shared");
Console.WriteLine(original.Tags.Count); // 2독립 컬렉션이 필요하면 with 안에서 새 컬렉션을 만듭니다. JSON DTO처럼 외부에서 가변 데이터를 받는 타입은 record라도 validation과 collection ownership을 따로 설계합니다.
선택 기준
| 필요 | 선택 | 확인할 점 |
|---|---|---|
| 값 기반 비교와 참조 타입 데이터 | record class | with는 얕은 복사 |
| 값 복사와 value equality | readonly record struct | 복사량과 nullable value 필요 여부 |
| identity·변경 이력·ORM entity lifecycle | class | equality를 명시적으로 설계 |
| 단순 작은 값, record 문법 불필요 | readonly struct | 데이터와 copy semantics만 필요할 때 |
record class가 heap allocation을, record struct가 stack allocation을 보장하는 것은 아닙니다. 타입 선언의 핵심 차이는 value/reference semantics와 generated equality이며, 실제 저장 위치는 runtime implementation과 사용 맥락에 달려 있습니다.
자주 틀리는 부분
hash 기반 collection의 key로 쓰는 record는 equality와 hash에 들어가는 멤버를 넣은 뒤 바꾸면 안 됩니다. mutable member를 변경하면 Dictionary나 HashSet에서 같은 key를 다시 찾지 못할 수 있습니다.
record는 DTO에 편리하지만 모든 도메인 entity를 record로 바꾸라는 뜻은 아닙니다. 식별자·수명·상태 전이가 핵심이면 명시적인 class API가 더 안전합니다.
참고 링크
2 sources