C#LAB
C# 2.3 · 2부. 객체지향

생성자와 초기화

객체가 만들어질 때 무엇을 갖추어야 하는지 정합니다.

예상 학습 시간 15분 실행 단추가 있는 예제는 고쳐서 실행해 볼 수 있습니다 난이도 기초
개념 설명

만들어질 때 갖추게 하기

지금까지는 객체를 만든 뒤에 값을 하나씩 담았습니다. 그러면 담기를 빠뜨려도 아무도 막지 않습니다. 이름 없는 학생, 값이 비어 있는 계좌가 만들어지고, 그것이 문제를 일으키는 것은 한참 뒤 엉뚱한 자리에서입니다.

생성자(constructor)는 객체가 만들어지는 그 순간에 실행되는 특별한 메서드입니다. 여기서 받을 것을 정해 두면 갖추지 않고는 만들 수 없게 됩니다.

  • 이름이 클래스 이름과 같습니다.
  • 반환 타입을 적지 않습니다void 도 적지 않습니다.
  • new 로 객체를 만들 때 저절로 불립니다.
최소 예제 · 편집 가능

받아야 만들어지는 클래스

아래 코드는 브라우저 안에서 실제로 실행됩니다. 고쳐서 눌러 보셔도 됩니다.

Program.cs
var a = new Student("홍길동", 90);
var b = new Student("김철수", 85);

Console.WriteLine(a.Describe());
Console.WriteLine(b.Describe());

class Student {
    public string Name { get; set; }
    public int Score { get; set; }

    // 이름이 클래스와 같고 반환 타입이 없습니다
    public Student(string name, int score) {
        Name = name;
        Score = score;
    }

    public string Describe() => $"{Name}: {Score}점";
}
출력
홍길동: 90점 김철수: 85점

코드는 고쳐서 실행해 볼 수 있습니다. 처음 누를 때만 실행기를 내려받느라 잠시 걸립니다. 적은 코드는 서버로 나가지 않습니다.

new Student("홍길동", 90) 한 줄로 만들기와 갖추기가 함께 끝납니다. 그리고 이제 이름 없는 학생은 만들 수 없습니다 — 빠뜨리면 컴파일러가 막습니다.

상세 사용법

만든 뒤에 담기와 만들면서 받기

만든 뒤에 담기
var s = new Student();
s.Name = "홍길동";
// Score 를 빠뜨렸습니다

// 아무도 막지 않습니다
// 0점인지 안 적은 것인지
// 알 수 없습니다
만들면서 받기
var s = new Student("홍길동");

// 컴파일 오류
// — 점수를 넘기지 않았습니다

// 갖추지 않으면
// 만들어지지 않습니다

생성자를 만들면 사라지는 것

생성자를 하나라도 적으면 new Student() 가 컴파일되지 않습니다. 아무것도 적지 않았을 때만 컴파일러가 빈 생성자를 대신 만들어 주기 때문입니다. 우리가 하나를 적는 순간 그 호의는 사라집니다.

처음 클래스를 만들 때 거의 모두가 한 번은 마주치는 오류입니다. 빈 생성자도 함께 두고 싶다면 직접 적어 주면 됩니다.

class Student {
    public string Name { get; set; }
    public int Score { get; set; }

    public Student() { }                     // 빈 것도 두려면 적습니다

    public Student(string name) : this(name, 0) { }   // 아래 것에 넘깁니다

    public Student(string name, int score) {
        Name = name;
        Score = score;
    }
}

가운데 것의 : this(name, 0)같은 클래스의 다른 생성자를 먼저 부르라는 뜻입니다. 담는 코드를 한 자리에 모아 두면 규칙이 바뀔 때 한 곳만 고치면 됩니다.

이름을 적어 담는 길

생성자에 넘기는 값이 여럿이면 new Student("홍길동", 90, 3, true) 처럼 되어 무엇이 무엇인지 알기 어려워집니다. 객체 초기화자를 사용하면 이름을 적어 담을 수 있습니다(C# 3.0+).

var s = new Student {
    Name = "홍길동",
    Score = 90
};

읽기는 좋지만 빠뜨려도 막지 못한다는 문제가 그대로 남습니다. 그래서 C# 11.0부터 required 가 생겼습니다. 붙여 두면 담지 않고는 만들 수 없습니다 — 초기화자의 읽기 좋음과 생성자의 강제함을 함께 얻습니다.

class Student {
    public required string Name { get; set; }
    public int Score { get; set; }
}

var a = new Student { Name = "홍길동" };   // 됩니다
var b = new Student { Score = 90 };        // 컴파일 오류 — Name 이 없습니다

받을 것을 이름 옆에 적기

밖에서 받은 것을 그대로 옮겨 담기만 하는 생성자가 대부분입니다. C# 12.0부터는 받을 것을 클래스 이름 옆에 적을 수 있고, 그 값을 클래스 안 어디서나 사용할 수 있습니다.

// 생성자를 따로 적지 않았습니다
class Student(string name, int score) {
    public string Name { get; } = name;
    public int Score { get; } = score;

    public string Describe() => $"{name}: {score}점";
}

어느 것을 쓸지는 무엇을 지키려는지에 달렸습니다. 반드시 갖춰야 할 것이 있으면 생성자나 required 를, 담을 것이 많고 대부분 선택이면 초기화자를 사용합니다.

버전 배지

이 문법, 몇 버전부터 사용할 수 있나요

1.0
2.0
3.0
6.0
8.0
9.0
11.0
12.0
14.0
  • 1.0생성자 · 오버로딩 · this()
  • 3.0객체 초기화자 — 이름을 적어 담기
  • 11.0required — 담지 않으면 못 만들게
  • 12.0기본 생성자 — 받을 것을 이름 옆에
C# 11.0+

생성자는 C# 1.0부터 있었습니다. 뒤에 더해진 것들은 모두 같은 물음을 다르게 풉니다 — 어떻게 하면 갖출 것을 갖추게 하면서도 짧게 적는가. 객체 초기화자는 읽기 좋게 했지만 강제하지 못했고, required 가 그 구멍을 메웠으며, 기본 생성자는 옮겨 담기만 하던 코드를 없앴습니다.

실습 문제

직접 해보기

1. 제목 없이는 만들 수 없는 책 난이도 하

Book 클래스에 제목과 쪽수를 두되, 제목은 반드시 받아야 만들어지게 해보세요. 쪽수는 넘기지 않으면 0이면 됩니다.

생성자를 둘 두고, 짧은 쪽이 긴 쪽에 : this(...) 로 넘기면 담는 코드가 한 자리에 모입니다.
var a = new Book("어린 왕자", 120); var b = new Book("홍길동전"); Console.WriteLine($"{a.Title} {a.Pages}쪽"); Console.WriteLine($"{b.Title} {b.Pages}쪽"); class Book { public string Title { get; set; } public int Pages { get; set; } public Book(string title) : this(title, 0) { } public Book(string title, int pages) { Title = title; Pages = pages; } }
2. 빠뜨리면 만들어지지 않게 난이도 중

같은 Book 을 이번에는 생성자 없이 만들되, 제목을 빠뜨리면 컴파일되지 않게 해보세요. 담을 때는 이름을 적어 담습니다.

반드시 담게 하는 표시는 required 입니다. 이름을 적어 담는 것은 객체 초기화자입니다.
var a = new Book { Title = "어린 왕자", Pages = 120 }; var b = new Book { Title = "홍길동전" }; // var c = new Book { Pages = 96 }; ← 컴파일 오류 Console.WriteLine($"{a.Title} {a.Pages}쪽"); Console.WriteLine($"{b.Title} {b.Pages}쪽"); class Book { public required string Title { get; set; } public int Pages { get; set; } }
요약
  • 생성자는 이름이 클래스와 같고 반환 타입이 없습니다.
  • 생성자를 하나라도 적으면 매개 변수 없는 생성자는 사라집니다. 필요하면 직접 적습니다.
  • 객체 초기화자는 읽기 좋지만 빠뜨림을 막지 못합니다. required 가 그것을 막습니다.
  • 옮겨 담기만 한다면 기본 생성자로 클래스 이름 옆에 적을 수 있습니다.

C# 버전별 변경 이력 에서 각 버전이 무엇을 더했는지 볼 수 있습니다.