객체지향 프로그래밍 특징 중 가장 중요하다고 언급한 상속 개념을 알아보겠습니다.
상속은 클래스를 만들었을 때 그 클래스의 기능을 다른 클래스에도 사용할 수 있도록 만드는 개념입니다.
상속을 쓰면 코드의 중복을 줄이고, 간결하게 작성할 수 있게 할 수 있어 유지보수하기에 유리합니다.
class 부모 클래스
{
}
class 자식 클래스 : 부모 클래스
{
}
이렇게 Slime과 Dragon 클래스에 아무것도 없지만 부모클래스인 Monster에서 기능을 상속을 받아서 사용합니다.
class Monster // 부모클래스
{
public int hp;
public string name;
public void Move()
{
Console.WriteLine("{0} 움직인다.");
}
}
class Slime : Monster // Monster라는 부모클래스에서 상속
{
}
class Dragon : Monster // Monster라는 부모클래스에서 상속
{
}
static void Main(string[] args)
{
Slime slime = new Slime();
Dragon dragon = new Dragon();
slime.Move();
dragon.Move();
}
접근제한자 중에 public과 private에 대해서는 알아보았지만
protected라는 접근제한자는 처음 들어보았을 것입니다.
이것은 상속 개념에서 볼 수 있는데
부모 클래스에서 상속받은 자식 클래스만 사용가능하도록 제한하는 기능입니다.
class Monster
{
protected int hp; // 상속받은 클래스만 사용하도록 제한
public string name; // 제한 없음
private int level // 외부 사용 완전 제한
}
this는 클래스 내부의 변수에 접근하는 경우 사용하고
base는 부모클래스의 변수에 접근하는 경우 사용합니다.
class Dragon : Monster
{
public float speed;
public Dragon()
{
name = "드래곤";
hp = 10000;
base.speed = 10f; // 부모클래스인 Monster 클래스의 speed에 접근
this.speed = 5.5f // Dragon 클래스의 speed에 접근
}
업캐스팅은 자식클래스가 부모클래스로 형변환하는 것을 의미하며,
흔히 상속 개념에서 자식 클래스가 부모 클래스의 기능을 사용할 수 있는 것을 생각하시면 됩니다
결론적으로 상속은 기본적으로 업캐스팅을 지원한다고 보시면 됩니다.
상위 클래스에서 하위 클래스로 형 변환을 하는것을 의미합니다.
상속은 업캐스팅을 지원하지만 다운캐스팅을 지원하지 않습니다.
간단히 말하자면 자식클래스에 있는 함수를 부모클래스에서 사용할 수 없는 것입니다.
사용을 하기 위해서는 As와 Is 개념이 필요합니다!
다운캐스팅이 성공한다면 객체를 반환하고 실패하면 null값을 반환하는 구문입니다.
다운캐스팅이 가능하다면 true, 불가능할 경우 false를 반환하는 구문입니다.
static void Main(string[] args)
{
object s = "반갑습니다.";
string r1 = s as string;
Console.WriteLine($"[1] {r1}");
object i = 1234;
string r2 = i as string;
if (r2 == null)
{
Console.WriteLine("[2] null 입니다.");
}
object i2 = 3456;
if (i2 is string)
{
string r3 = i2 as string;
Console.WriteLine($"[3] {r3}");
}
else
{
Console.WriteLine("[3] 변환 불가");
}
}
[1] 반갑습니다.
[2] null 입니다.
[3] 변환 불가
internal class Program
{
class Movie
{
public virtual void Position()
{
Console.WriteLine("CGV에 가자");
}
}
class Coco : Movie
{
public override void Position()
{
Console.WriteLine("Coco 보러 CGV에 가자");
}
public void CocoScore()
{
Console.WriteLine(95);
}
}
class Soul : Movie
{
public override void Position()
{
Console.WriteLine("Soul 보러 CGV에 가자");
}
public void SoulScore()
{
Console.WriteLine(90);
}
}
static void Main(string[] args)
{
Movie[] movies =
{
new Coco(),
new Soul()
};
for (int i = 0; i < movies.Length; i++)
{
movies[i].Position();
// IS : 다운캐스팅이 가능하다면 true, 불가능할 경우 false를 반환
if (movies[i] is Coco)
{
// As : 다운캐스팅이 성공한다면 객체를 반환하고 실패하면 null값을 반환
(movies[i] as Coco).CocoScore();
}
else if (movies[i] is Soul)
{
(movies[i] as Soul).SoulScore();
}
}
}
}
Coco 보러 CGV에 가자
95
Soul 보러 CGV에 가자
90
앞에 코드에서 if문 안쓰고 ?를 이용하여 간결하게 나타낼 수도 있다.
?는 null인지 아닌지 판단하는 구문으로 조건문의 성격을 가진 연산자입니다!
(movies[i] as Coco)?.CocoScore();
(movies[i] as Soul)?.SoulScore();
해당 클래스를 상속시키지 않도록 만들어주는 구문입니다.
보통 프로젝트를 할 때 해당 클래스를 상속하지 않고 단일로 사용하고자 할 때 사용하는 문법입니다.
sealed class cat
{
{
같은 이름의 메서드를 여러 개 정의하는 기능입니다.
보통 함수의 경우 만들어둔 변수와 같은 변수로 사용할 경우 오류가 발생하는데
클래서 내부에서는 매개변수가 다른 경우 서로 다른 메서드로 인식하여 같은 이름으로 만들 수 있습니다!
class Cat
{
public string name = null;
public int Weight = 0;
public Cat(string catName)
{
name = catName;
Console.WriteLine("고양이의 이름은 " + name + "입니다. ");
}
public Cat(string catName, int catWeight) // 메서드명은 같지만 매개변수가 달라 작동하고 있음
{
name = catName;
Weight = catWeight;
Console.WriteLine("고양이의 이름은 " + name + "이며, 몸무게는 " + Weight + "Kg입니다.");
}
}
static void Main(string[] args)
{
Cat jemmy = new Cat("지미");
Cat coco = new Cat("코코", 3);
}
고양이의 이름은 지미입니다.
고양이의 이름은 코코이며, 몸무게는 3Kg입니다.
부모클래스에 있는 메서드를 자식클래스에서 재정의하는 기능입니다
만약 NPC를 만들 때 청소부, 구급대원을 만들 경우 하는 일이 달라 움직이는 매커니즘을 다르게 해야하는데 이 경우 각각 메서드명을 달리하면 유지보수하기 상당히 힘들 것이기 때문에
부모 클래스에 있는 메서드의 이름을 그대로 가져와서 재정의를 할 수 있도록 할 수 있도록 만들 수 있는 편리한 기능입니다.
부모클래스에서 virtual을 붙여 가상 메서드로 만들고 자식클래스에서 override를 붙여 오버라이딩을 한다
꼭 재정의할 때에 메서드 이름과 매개변수 개수, 반환형은 같아야 한다!
class Npc
{
public virtual void Move() // 부모클래스에서 virtual을 붙여 가상 메서드로 만든다.
{
Console.WriteLine("이동합니다.");
}
}
class CleanMan : Npc
{
public override void Move() // // 자식클래스에서 override를 붙여 오버라이딩을 한다
{
Console.WriteLine("쓰레기차로 이동합니다.");
}
}
class AmbulanceWorker : Npc
{
public override void Move()
{
Console.WriteLine("구급차로 이동합니다.");
}
}
static void Main(string[] args)
{
CleanMan cleanMan = new CleanMan();
AmbulanceWorker worker = new AmbulanceWorker();
cleanMan.Move();
worker.Move();
}
쓰레기차로 이동합니다.
구급차로 이동합니다.
오버라이딩을 안할 경우 상속은 받아 사용할 수는 있지만
부모클래스에 있는 내용이 출력되는 것을 알 수 있습니다.
internal class Program
{
class Movie
{
public void Position()
{
Console.WriteLine("CGV에 가자");
}
}
class Coco : Movie
{
public void Position()
{
Console.WriteLine("Coco 보러 CGV에 가자");
}
public void CocoScore()
{
Console.WriteLine(95);
}
}
class Soul : Movie
{
public void Position()
{
Console.WriteLine("Soul 보러 CGV에 가자");
}
public void SoulScore()
{
Console.WriteLine(90);
}
}
static void Main(string[] args)
{
Movie coco1 = new Coco();
Coco coco2 = new Coco();
coco1.Position();
coco2.Position();
Movie soul1 = new Soul();
Soul soul2 = new Soul();
soul1.Position();
soul2.Position();
}
}
CGV에 가자
Coco 보러 CGV에 가자
CGV에 가자
Soul 보러 CGV에 가자
오버라이딩을 한 경우에 정상적으로 자식클래스의 구문이 활성화 되는 것을 볼 수 있습니다!
internal class Program
{
class Movie
{
public virtual void Position()
{
Console.WriteLine("CGV에 가자");
}
}
class Coco : Movie
{
public override void Position()
{
Console.WriteLine("Coco 보러 CGV에 가자");
}
public void CocoScore()
{
Console.WriteLine(95);
}
}
class Soul : Movie
{
public override void Position()
{
Console.WriteLine("Soul 보러 CGV에 가자");
}
public void SoulScore()
{
Console.WriteLine(90);
}
}
static void Main(string[] args)
{
Movie coco1 = new Coco();
Coco coco2 = new Coco();
coco1.Position();
coco2.Position();
Movie soul1 = new Soul();
Soul soul2 = new Soul();
soul1.Position();
soul2.Position();
}
}
Coco 보러 CGV에 가자
Coco 보러 CGV에 가자
Soul 보러 CGV에 가자
Soul 보러 CGV에 가자