수업을 들으며 노트에 정리한 것 (상속)
개요
비슷한 클래스를 여러 개 만들다 보면 같은 코드가 반복된다. 상속은 이미 만든 클래스의 멤버를 물려받아 새 클래스를 만드는 문법이다. 물려받은 쪽은 차이 나는 부분만 적으면 된다.
객체 지향 언어의 필수 (상속)
유지 보수의 베이스
상속의 필요
- 같은 코드가 반복 되는 자리
공통부를 따로 떼기
- 공통 멤버를 담은 클래스 하나를 만들고, 나머지는 그것을 물려받으면 된다.
상속 문법
- 클래스 이름 뒤에 콜론
(:)과 물려받을 클래스 이름을 적는다
class 자식클래스 : 부모클래스
- 물려주는 쪽과 물려받는 쪽은 부르는 이름이 여러가지로 문서마다 다른 말을 쓰니 모두 알아 둘 필요가 있다.
(영어까지 외우기)
| 물려주는 쪽 | 물려받는 쪽 |
|---|---|
| 부모 클래스 (parent class) | 자식 클래스 (child class) |
| 기반 클래스 (base class) | 파생 클래스 (derived class) |
| 상위 클래스 (super class) | 하위 클래스 (sub class) |
물려받는 것과 물려받지 않는 것
| 멤버 | 물려받는가 |
|---|---|
| 필드 | 물려받음 |
| 프로퍼티 | 물려받음 |
| 메서드 | 물려받음 |
| 인덱서 | 물려받음 |
| 생성자 | 물려받지않음 |
물려받되 보이지 않는 것
private멤버도 자식 객체 안에 함께 만들어진다. 다만 자식 클래스에서는 닿을 수 없다.
protected
private자신의 클래스 안에서만 사용 가능public아무에게나 열려있음protected자식에게만 열고 바깥에서는 불가능private와public사이에 있는 중간 단계가protected다.
| 접근 모디파이어 | 클래스 안 | 자식 클래스 | 바깥 |
|---|---|---|---|
private | ○ | ✕ | ✕ |
protected | ○ | ○ | ✕ |
public | ○ | ○ | ○ |
protected 필드는 자식이 직접 값을 바꿀 수 있다는 뜻이므로, 검사가 필요한 값이면 필드를
private으로 두고 protected 프로퍼티나 메서드로 통로를 내는 편이 안전함.
생성자와 상속
- 생성자는 물려 받지 않는다.
- 부모에 매개변수가 있는 생성자만 있으면 자식은 그것을 그냥 쓸 수 없다.
Student student = new Student();
class Person
{
public string Name;
public Person(string name)
{
Name = name;
}
}
class Student : Person
{
}
- 자식이 생성자를 따로 정의하고, 부모 몫의 초기화는 부모 생성자에게 넘겨야 한다.
base()로 부모 생성자 부르기
- 자식 생성자의 매개변수 목록 뒤에
: (base(...)를 붙이면 부모 생성자가 먼저 실행된다.
using System;
Student student = new Student("홍길동", 3);
Console.WriteLine($"이름: {student.Name}, 학년: {student.Grade}");
class Person
{
public string Name;
public Person(string name)
{
Name = name;
Console.WriteLine($"Person 생성자: {name}");
}
}
class Student : Person
{
public int Grade;
public Student(string name, int grade) : base(name)
{
Grade = grade;
Console.WriteLine($"Student 생성자: {grade}학년");
}
}
Person 생성자: 홍길동
Student 생성자: 3학년
이름: 홍길동, 학년: 3
출력 순서를 보면 부모 생성자가 먼저 끝나고 자식 생성자 본문이 실행됨. 자식이 부모 필드를 만지기 전에 부모 쪽이 준비를 끝내야 하기 때문임.
base()를 생략하면
- base(...) 나 this(...)를 적지 않은 인스턴스 생성자는 부모의 매개변수 없는 생성자를 암시적으로 호출한다.
접근 가능한 해당 생성자가 없으면 인수를 받는 부모 생성자를 명시해야 한다.
부모에 매개변수 없는 생성자가 없으면 이 자동 호출이 실패하므로, 그때는 base()를 반드시 적어야 함.
여러 단계의 상속
- 상속은 여러 단계로 이어질 수 있으며, 생성자는 가장 위쪽 부모부터 차례로 실행된다.
자식 쪽 필드 이니셜라이저가 먼저 돌고, 그 다음 부모의 필드와 생성자, 마지막에 자식 생성자 본문이 실행됨.
📝 메모: 물려받은 멤버는 base.없이 이름만으로 씀. base를 반드시 적어야 하는 대표적인 경우가 여기서 다룬 base()생성자 호출임
base()와 this()
- 생성자 앞에 붙일 수 있는 것은 둘 중 하나다.
this()는 같은 클래스의 다른 생성자base()는 부모 생성자
상속 계층 설계
- is-a관계 / 상속은 "자식은 부모의 한 종류다" 가 성립할때 사용된다.
| 관계 | 성립 여부 |
|---|---|
| 개는 동물이다 | 성립 — class Dog : Animal |
| 학생은 사람이다 | 성립 — class Student : Person |
| 정사각형은 도형이다 | 성립 — class Square : Shape |
| 자동차는 엔진이다 | 성립하지 않음 |
| 주문은 상품이다 | 성립하지 않음 |
말이 되지 않는데도 코드가 겹친다는 이유로 상속하면, 자식이 쓸 일 없는 멤버를 물려받게 되고 부모를 고칠 때마다 관계없는 자식이 깨짐.
상속 대신 조합
- “
은이다”가 아니라 “**은을 가진다”라면 상속이 아니라 필드로 가지는 것**이 맞다. 이것을 조합이라고 부른다.
| 판단 | 문법 |
|---|---|
| 자식은 부모의 한 종류다 (is-a) | 상속 — class Dog : Animal |
| 이것은 저것을 가진다 (has-a) | 조합 — private Engine engine; |
C#에서 클래스는 부모를 하나만 가질 수 있다.
sealed클래스
- 더 이상 물려줄 일이 없는 클래스에는
sealed를 붙여 상속을 막을 수 있다.
네임스페이스
- 프로그램이 커지면 서로 다른 목적의 클래스가 같은 이름을 원하게 된다.
- 네임스페이스는 타입을 묶어 이름의 범위를 나누는 이름표다.
계층 구조
- 네임스페이스는 점으로 이어 계층을 만든다.
using System;
Game.Characters.Hero hero = new Game.Characters.Hero();
hero.Show();
Game.Items.Sword sword = new Game.Items.Sword();
sword.Show();
namespace Game.Characters
{
class Hero
{
public void Show()
{
Console.WriteLine("Game.Characters.Hero");
}
}
}
namespace Game.Items
{
class Sword
{
public void Show()
{
Console.WriteLine("Game.Items.Sword");
}
}
}
이름은 회사명.제품명.기능형태로 짓고 PascalCase를 씀.
폴더 구조와 네임스페이스를 맞춰 두면 파일을 찾기 쉬워짐.
MyGame/
├── Characters/
│ ├── Hero.cs → namespace MyGame.Characters
│ └── Enemy.cs → namespace MyGame.Characters
└── Items/
└── Sword.cs → namespace MyGame.Items
오늘은 C#언어에서 중요한 상속에 대하여 배웠다.
상속을 조금씩 이해하고, 과거에 했던 공부들을 복습하다보니 다시금 문제가 조금씩 풀려가기 시작했다.
지금의 내 모습이 아직 만족스럽지는 않지만 뿌듯한 감정은 든다.
이 모습 그대로 변하지 않고 꾸준히 성장했으면 좋겠다.



