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

클래스와 객체

틀(클래스)과 그것으로 찍어 낸 것(객체)의 관계입니다.

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

흩어진 값을 하나로 묶기

학생 한 명의 이름과 점수를 다루려면 변수 두 개면 됩니다. 그런데 학생이 셋으로 늘면 변수가 여섯 개가 되고, 어느 이름이 어느 점수와 짝인지는 변수 이름 끝의 숫자로만 짐작하게 됩니다. 짝을 잘못 맞춰도 컴파일러는 알려 주지 않습니다.

클래스는 함께 다녀야 할 값들을 하나로 묶는 틀입니다. 그 틀로 찍어 낸 것 하나하나를 객체(object) 또는 인스턴스라고 부릅니다.

  • 클래스 — 무엇을 담고 무엇을 할 수 있는지 정한 입니다. 하나만 적습니다.
  • 객체 — 그 클래스로 실제로 만들어 낸 실체입니다. 얼마든지 만들 수 있고, 저마다 자기 값을 가집니다.

붕어빵 틀과 붕어빵에 견주면 쉽습니다. 틀은 하나지만 붕어빵은 여럿이고, 각각에 든 팥의 양은 서로 다를 수 있습니다.

최소 예제 · 편집 가능

클래스를 만들고 객체 찍어 내기

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

Program.cs
var a = new Student();
a.Name = "홍길동";
a.Score = 90;

var b = new Student();
b.Name = "김철수";
b.Score = 85;

Console.WriteLine($"{a.Name}: {a.Score}점");
Console.WriteLine($"{b.Name}: {b.Score}점");

// 타입 선언은 최상위 문 뒤에 옵니다
class Student {
    public string Name;
    public int Score;
}
출력
홍길동: 90점 김철수: 85점

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

class Student { ... }클래스 선언이고, new Student() 가 그것으로 객체 하나를 찍어 내는 일입니다. ab 는 같은 클래스에서 나왔지만 서로 다른 객체라, 한쪽의 이름을 바꿔도 다른 쪽은 그대로입니다.

타입 선언이 맨 아래에 있는 것을 눈여겨보세요. 최상위 문을 사용하는 파일에서는 실행할 문장이 먼저 오고 클래스 선언이 그 뒤에 와야 합니다. 순서를 뒤집으면 컴파일되지 않습니다.

상세 사용법

왜 묶는가

클래스를 쓰지 않아도 같은 일을 할 수는 있습니다. 다만 다루는 것이 늘어날수록 짝을 사람이 기억해야 하는 코드가 됩니다.

묶지 않으면
string name1 = "홍길동";
int score1 = 90;

string name2 = "김철수";
int score2 = 85;

// 짝은 이름 끝의 숫자로만 압니다
// 셋째가 늘면 변수도 둘 늘어납니다
묶으면
var list = new List<Student>();

list.Add(a);
list.Add(b);

// 짝이 흩어지지 않습니다
// 몇 명이 늘어도 담는 곳은 하나

묶어 두면 여럿을 한 자리에 담을 수 있다는 것이 특히 큽니다. 이름과 점수가 따로 놀면 목록으로 만들 수도 없습니다.

클래스는 참조 형식입니다

1.3에서 값 형식과 참조 형식의 차이를 보았습니다. 클래스로 만든 객체는 참조 형식이라, 다른 변수에 담으면 값이 복사되는 것이 아니라 같은 것을 가리키게 됩니다.

var a = new Student();
a.Name = "홍길동";

var b = a;              // 위치를 복사 — 새로 찍어 낸 것이 아닙니다
b.Name = "김철수";

Console.WriteLine(a.Name);   // 김철수 — a 도 함께 바뀌었습니다

새로 하나 더 만들려면 new 를 다시 불러야 합니다. = 로 담는 것은 이름을 하나 더 붙이는 일일 뿐입니다.

클래스 안에 하는 일도 담습니다

클래스에는 값만이 아니라 그 값으로 하는 일도 담을 수 있습니다. 1.8에서 만든 메서드를 클래스 안에 두면, 그 객체의 값을 그대로 사용할 수 있습니다.

class Student {
    public string Name;
    public int Score;

    // 매개 변수로 받지 않아도 제 값을 압니다
    public string Describe() {
        return $"{Name}: {Score}점";
    }
}

Console.WriteLine(a.Describe());   // 홍길동: 90점

Describe 안의 Name자기를 부른 객체의 이름입니다. a.Describe() 는 a의 이름을, b.Describe() 는 b의 이름을 사용합니다. 이것을 분명히 적고 싶을 때는 this.Name 이라고 합니다.

지금은 값을 public 필드로 그대로 드러냈습니다. 밖에서 아무 값이나 넣을 수 있다는 뜻이라 실제로는 이렇게 두지 않는데, 그것을 다루는 것이 바로 다음 단원입니다.

버전 배지

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

1.0
2.0
3.0
6.0
8.0
9.0
10.0
12.0
14.0
  • 1.0클래스 · 객체 · 메서드
  • 9.0record — 값을 담는 형식을 한 줄로
  • 12.0기본 생성자 — 받을 것을 이름 옆에
C# 1.0+

클래스와 객체는 C# 1.0부터 있었고 지금까지 뼈대가 바뀌지 않았습니다. 뒤에 더해진 것은 같은 일을 짧게 적는 길입니다 — 값을 담기만 하는 형식을 위한 record(C# 9.0)와, 받을 것을 클래스 이름 옆에 적는 기본 생성자(C# 12.0)가 그렇습니다. 둘 다 이 단원 뒤에 만납니다.

실습 문제

직접 해보기

1. 책 한 권을 담는 클래스 만들기 난이도 하

제목(string)과 쪽수(int)를 담는 Book 클래스를 만들고, 책 두 권을 만들어 각각 출력해보세요.

클래스는 한 번만 적고 new 를 두 번 부릅니다. 최상위 문을 사용하는 파일이라면 class 선언은 맨 아래에 두어야 합니다.
var a = new Book(); a.Title = "어린 왕자"; a.Pages = 120; var b = new Book(); b.Title = "홍길동전"; b.Pages = 96; Console.WriteLine($"{a.Title} ({a.Pages}쪽)"); Console.WriteLine($"{b.Title} ({b.Pages}쪽)"); class Book { public string Title; public int Pages; }
2. 목록에 담고 한꺼번에 탐색하기 난이도 중

앞에서 만든 BookList<Book> 에 세 권 담고, 쪽수가 100쪽을 넘는 책의 제목만 출력해보세요. 클래스 안에 설명을 돌려주는 메서드를 두어도 좋습니다.

1.6의 foreach 와 1.5의 if 를 그대로 사용하면 됩니다. 담는 곳이 하나이므로 책이 몇 권이든 탐색하는 코드는 같습니다.
var books = new List<Book>(); books.Add(new Book { Title = "어린 왕자", Pages = 120 }); books.Add(new Book { Title = "홍길동전", Pages = 96 }); books.Add(new Book { Title = "토지", Pages = 840 }); foreach (Book book in books) { if (book.Pages > 100) { Console.WriteLine(book.Title); } } class Book { public string Title; public int Pages; }
요약
  • 클래스는 틀이고, new 로 찍어 낸 하나하나가 객체입니다.
  • 객체마다 자기 값을 가집니다. 한쪽을 바꿔도 다른 쪽은 그대로입니다.
  • 클래스는 참조 형식이라, = 로 담으면 이름만 하나 더 붙습니다.
  • 최상위 문을 사용하는 파일에서는 클래스 선언을 맨 아래에 둡니다.

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