C# 12 Primary Constructors — 간결한 생성자 선언
Primary Constructors는 C# 12에서 클래스와 구조체에 도입된 기능으로, 생성자 매개변수를 타입 선언부에 직접 정의합니다. 이전에는 레코드(record)에서만 가능했던 문법이 일반 클래스/구조체로 확대되었습니다.
1. 기본 문법
섹션 제목: “1. 기본 문법”// Before (C# 11 이하)public class OrderService{ private readonly IOrderRepository _repo; private readonly ILogger<OrderService> _logger;
public OrderService(IOrderRepository repo, ILogger<OrderService> logger) { _repo = repo; _logger = logger; }}
// After (C# 12)public class OrderService(IOrderRepository repo, ILogger<OrderService> logger){ // repo, logger를 클래스 전체에서 직접 사용 public async Task<Order?> GetAsync(int id) { logger.LogInformation("Getting order {Id}", id); return await repo.FindAsync(id); }}2. 매개변수 캡처와 필드 초기화
섹션 제목: “2. 매개변수 캡처와 필드 초기화”Primary Constructor 매개변수는 필드가 아닙니다. 필드로 저장하려면 명시적으로 할당해야 합니다.
public class Cache(int capacity){ // 매개변수를 필드로 저장 private readonly int _capacity = capacity;
// 매개변수를 이용한 필드 초기화 private readonly Dictionary<string, object> _store = new(capacity);}3. 기반 클래스 생성자 호출
섹션 제목: “3. 기반 클래스 생성자 호출”public class Animal(string name){ public string Name { get; } = name;}
public class Dog(string name, string breed) : Animal(name){ public string Breed { get; } = breed;}4. DI 컨테이너 패턴
섹션 제목: “4. DI 컨테이너 패턴”// 기존 DI 패턴과 동일하게 동작public class UserController( IUserService userService, ILogger<UserController> logger, IMapper mapper){ [HttpGet("{id}")] public async Task<IActionResult> Get(int id) { logger.LogDebug("Fetching user {Id}", id); var user = await userService.GetByIdAsync(id); return user is null ? NotFound() : Ok(mapper.Map<UserDto>(user)); }}5. 구조체에서의 활용
섹션 제목: “5. 구조체에서의 활용”public struct Point(double x, double y){ public double X { get; } = x; public double Y { get; } = y; public double Distance => Math.Sqrt(X * X + Y * Y);}
var p = new Point(3, 4);Console.WriteLine(p.Distance); // 56. 주의사항 — 매개변수 변이
섹션 제목: “6. 주의사항 — 매개변수 변이”public class Counter(int start){ private int _count = start; // start를 캡처해 필드 초기화
public void Increment() => _count++;
// 주의: start는 여전히 접근 가능하지만 _count와 다를 수 있음 public int Initial => start; public int Current => _count;}매개변수를 여러 곳에서 사용하면 값이 분기될 수 있습니다. 혼동을 피하려면 필드에 저장 후 매개변수 참조를 제한하세요.
7. record vs class Primary Constructor
섹션 제목: “7. record vs class Primary Constructor”| 항목 | record | class |
|---|---|---|
| 매개변수 → 속성 자동 생성 | ✓ | ✗ (직접 선언) |
with 표현식 | ✓ | ✗ |
Equals/GetHashCode 자동 생성 | ✓ | ✗ |
| 불변성 기본 | ✓ | ✗ |
Primary Constructors는 DI 의존성 주입, 간단한 값 객체, 서비스 클래스에서 생성자 보일러플레이트를 크게 줄여줍니다. 단, 매개변수가 필드가 아님을 명심하고 필요한 경우 명시적으로 필드에 저장하세요.