Quick Reference
생성자는 객체가 유효한 상태로 만들어지는 한 경로를 정의합니다. 필수값은 생성자에서 받고, 입력 조합만 다른 오버로드는 this(...)로 한 생성자에 모읍니다. static constructor는 한 번뿐인 타입 초기화에만 쓰며, 비동기 I/O나 실패 가능한 준비는 CreateAsync 같은 factory로 분리합니다.
public class Player
{
public string Name { get; }
public int Level { get; private set; }
public Player() : this("Guest", 1) { }
public Player(string name) : this(name, 1) { }
public Player(string name, int level)
{
Name = name;
Level = level;
}
}
var guest = new Player();
var beginner = new Player("Mina");
var veteran = new Player("Jin", 50);문법
어떤 생성 형태가 있나
| 형태 | 언제 떠올리나 |
|---|---|
| 매개변수 없는 생성자 | 프레임워크/직렬화 요구가 있을 때 |
| 오버로드 생성자 | 필수값은 같고 입력 조합만 다를 때 |
: this(...) 체이닝 | 초기화 규칙을 한 곳에 모을 때 |
| primary constructor | 클래스 선언이 짧고 의존성 주입이 단순할 때 |
| 정적 생성자 | 타입 수준 준비 작업이 한 번만 필요할 때 |
생성자의 역할: 처음부터 유효한 상태
생성자의 핵심 목적은 객체가 만들어지는 순간 모순 없는 초기 상태를 보장하는 것입니다. 필수 데이터를 생성자 매개변수로 받으면, 불완전한 상태로 존재하는 객체를 원천 차단할 수 있습니다.
// ❌ 필수 값이 없어도 만들 수 있고, 나중에 설정을 깜빡할 수 있음
var order = new Order();
order.CustomerId = 42; // 설정하지 않으면 OrderId가 0인 채로 사용됨
// ✅ 필수 값 없이는 객체 생성 자체가 불가
var order = new Order(customerId: 42, productId: 7);생성자 오버로딩
같은 타입을 다양한 방식으로 초기화할 때 씁니다. 각 오버로드는 서로 다른 매개변수 조합을 받습니다.
public class Rect
{
public double Width, Height;
public Rect(double size) : this(size, size) { } // 정사각형
public Rect(double width, double height)
{
Width = width; Height = height;
}
}this() — 생성자 체이닝
: this(...) 로 같은 클래스의 다른 생성자를 먼저 호출합니다. 초기화 로직이 여러 생성자에 중복되는 것을 막는 가장 간단한 방법입니다.
public Player(string name) : this(name, level: 1) { }
// ↑ 먼저 Player(string, int)를 호출한 뒤 이 본문 실행// ❌ 각 생성자마다 검증 로직을 반복
public User(string name)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException();
Name = name;
Role = "user";
}
public User(string name, string role)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException();
Name = name;
Role = role;
}
// ✅ 공통 초기화는 한 생성자에 모음
public User(string name) : this(name, "user") { }
public User(string name, string role)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException();
Name = name;
Role = role;
}Primary constructor (C# 12+)
클래스 선언줄에 매개변수를 바로 씁니다. 매개변수는 인스턴스 멤버에서 참조할 수 있지만 자동 필드나 public 프로퍼티는 아닙니다. 단순한 서비스 주입에는 간결하지만, 외부 공개 상태·직렬화 모델·명시적 readonly 저장소가 필요하면 일반 생성자 또는 명시적 멤버를 고릅니다.
public class OrderService(IRepository repo, ILogger logger)
{
public void Process(Order order)
{
logger.Log("processing");
repo.Save(order);
}
}이 카드에서는 생성 순서와 수명만 다루고, non-record primary constructor의 캡처·record와의 차이는 primary-constructors 카드에서 이어서 확인합니다.
인스턴스 초기화 순서
new Derived()가 실행되면 필드 기본값, 선언한 필드 초기화식, base type 초기화와 base constructor, 현재 생성자 본문이 순서대로 진행되고 object initializer는 생성자 뒤에 실행됩니다. 생성자에서 virtual 메서드를 호출하면 파생 타입 필드가 아직 원하는 상태가 아닐 수 있으므로 피합니다.
public class Base
{
protected readonly string Name;
public Base(string name) => Name = name;
}
public class Player : Base
{
private readonly int _level = 1; // base constructor보다 먼저 초기화
public Player(string name) : base(name)
{
// 여기서는 Name과 _level을 사용할 수 있다.
}
}
var player = new Player("Mina") { }; // object initializer는 생성자 뒤에 적용정적 생성자
인스턴스가 처음 만들어지기 전 또는 해당 타입의 정적 멤버를 처음 쓰기 전에 런타임이 최대 한 번 호출합니다. 호출 시점은 코드가 직접 제어할 수 없으며, 명시적 static constructor가 있으면 beforefieldinit 최적화가 적용되지 않습니다. 정적 필드 초기화처럼 짧고 결정적인 준비 작업에만 씁니다.
public class Config
{
public static readonly string AppName;
static Config()
{
AppName = Environment.GetEnvironmentVariable("APP_NAME") ?? "RefDock";
}
}생성자 형태 선택
| 패턴 | 설명 |
|---|---|
| 기본 생성자 | 매개변수 없음. 클래스에 생성자가 없으면 컴파일러가 자동 생성 |
| 오버로딩 | 다양한 초기화 방식을 제공 |
: this(...) | 중복 초기화 코드를 한 곳에 모음 |
primary constructor | 선언줄 매개변수, C# 12+ |
static 생성자 | 클래스당 한 번, 타입 초기화 전용 |
주의할 점
생성자에서 너무 많은 일을 하면 테스트와 재사용이 어려워집니다. 파일 읽기, 네트워크 요청처럼 실패 가능성이 있는 작업은 생성자보다 팩토리 메서드나 초기화 메서드로 빼는 편이 좋습니다.
정적 생성자에서 예외가 발생하면 해당 타입은 TypeInitializationException으로 감싸인 채 프로세스가 살아있는 동안 다시는 초기화되지 않습니다. 정적 생성자는 가능한 한 단순하게 유지하세요.
매개변수 없는 생성자를 편의상 추가하면 "불완전한 상태의 객체"가 다시 들어올 수 있습니다. ORM이나 serializer 같은 외부 요구가 아니라면, 필수 값이 있는 타입은 생성자에서도 그 요구를 그대로 드러내는 편이 안전합니다.
public sealed class Config(string value)
{
public static async Task<Config> CreateAsync(HttpClient http, CancellationToken ct)
{
string loaded = await http.GetStringAsync("config", ct);
return new Config(loaded);
}
}참고 링크
2 sources