https://blog.naver.com/bamsunbic/221370090169
기반클래스와 파생클래스 사이에서는 족보를 오르내리는 형식 변환이 가능
파생 클래스의 인스턴스는 기반 클래스의 인스턴스로서도 사용 가능
먼저 왜 기반, 파생 사이의 형변환이 필요한지부터 알아보자.
1. 다형성과 유연성
가장 중요한 이유이다. 다형성은 코드의 재사용성과 유연성을 향상시키며, 객체 간의 관계를 간결하게 표현할 수 있다.
2. 멤버의 개수 조절
여러자료를 찾아본 결과 형변환은 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 개수를 조절하는 역할을 한다.
0. 이것이 C#이다 책의 설명
여태까지 Mammal mammal = new Mammal(); 만 했다 (포유류 = 포유류)
이 포유류 클래스를 상속받는 Dog, Cat등등 파생클래스를 만들었다
여태까지는 파생클래스도 Dog dog = new Dog();으로만 썼음(개 = 개)
근데, (개 = 포유류, 고양이 = 포유류) 식의 코드도 가능하다
Mammal mammal = new Mammal();
mammal = new Dog(); //(개 = 포유류(참))
mammal.Nurse();
Dog dog = (Dog)mammal;
dog.Nurse();
dog.Bark();
----------------------------------------------여기까지 책에 나온 형변환 개요---------------------------------------------
1. 다형성과 유연성
기반 클래스와 파생 클래스 사이의 형변환은 다형성을 활용하고 객체 지향 프로그래밍의 중요한 개념 중 하나입니다. 다형성은 코드의 재사용성과 유연성을 향상시키며, 객체 간의 관계를 간결하게 표현할 수 있습니다. 이러한 이유로 기반 클래스와 파생 클래스 사이의 형변환을 사용합니다.
다형성을 이해하기 위해 예를 들어보겠습니다. 가정해보겠습니다. 우리는 기반 클래스인 `Shape`과 이를 상속받은 파생 클래스인 `Circle`과 `Rectangle`이 있다고 가정합니다. `Shape` 클래스에는 `Draw()` 메서드가 정의되어 있습니다.
public class Shape
{
public virtual void Draw()
{
Console.WriteLine("Drawing a shape");
}
}
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle");
}
}
이제 다형성을 활용하여 기반 클래스와 파생 클래스 간의 형변환을 살펴보겠습니다.
Shape circle = new Circle();
Shape rectangle = new Rectangle();
위의 코드에서 `Circle` 클래스와 `Rectangle` 클래스는 모두 `Shape` 클래스를 상속받았습니다. 따라서 `Circle`과 `Rectangle`은 `Shape` 타입으로 선언된 변수에 할당될 수 있습니다.
이렇게 기반 클래스 타입의 변수에 파생 클래스의 인스턴스를 할당하는 것을 업캐스팅(upcasting)이라고 합니다. 업캐스팅을 통해 파생 클래스의 인스턴스를 기반 클래스의 변수로 다룰 수 있습니다.
이제 `Draw()` 메서드를 호출해 보겠습니다.
circle.Draw(); // "Drawing a circle"
rectangle.Draw(); // "Drawing a rectangle"
위의 예시에서 `Draw()` 메서드는 `Shape` 클래스에서 정의되었지만, 실제로 실행되는 메서드는 해당 인스턴스의 실제 타입에 따라 달라집니다. 이것이 다형성의 핵심입니다. 업캐스팅된 변수를 통해 메서드를 호출하더라도 실제 인스턴스의 오버라이딩된 메서드가 실행됩니다.
이러한 다형성을 통해 코드의 유연성과 재사용성이 향상됩니다. 예를 들어, `Shape` 타입의 컬렉션을 사용하면 `Circle`, `Rectangle`, 그리고 기타 `Shape`의 파생 클래스 인스턴스들을 모두 관리할 수 있습니다. 이렇게 다형성을 활용하면 코드를 확장하고 변경하기가 더 쉬워집니다.
그럼 다운캐스팅은 왜 필요할까
다운캐스팅은 업캐스팅과 반대로, 기반 클래스로부터 파생된 클래스의 인스턴스를 다시 해당 파생 클래스의 타입으로 형변환하는 과정을 말합니다. 다운캐스팅은 주로 업캐스팅된 객체를 다시 원래의 파생 클래스로 사용해야 할 때 필요합니다.
일반적으로 다운캐스팅은 다음과 같은 상황에서 사용될 수 있습니다:
1. 업캐스팅된 객체를 원래의 파생 클래스 타입으로 사용해야 할 때: 업캐스팅을 통해 기반 클래스로 타입이 변경된 객체는 기반 클래스의 멤버만 접근할 수 있습니다. 하지만 때로는 객체를 다시 파생 클래스로 사용해야 하는 상황이 있을 수 있습니다. 이때 다운캐스팅을 통해 해당 객체를 원래의 파생 클래스 타입으로 변환하여 파생 클래스의 멤버에 접근할 수 있습니다.
2. 런타임에 객체의 실제 타입을 확인하고 사용해야 할 때: 업캐스팅된 객체를 어떤 파생 클래스로 형변환할 수 있는지 런타임에 결정해야 할 때가 있습니다. 이때 다운캐스팅을 사용하여 런타임에 객체의 실제 타입을 확인하고 해당 타입으로 형변환하여 사용할 수 있습니다.
하지만 다운캐스팅은 주의해야 할 점이 있습니다. 다운캐스팅을 할 때는 실제로 해당 타입으로 변환할 수 있는지 확인하는 것이 중요합니다. 그렇지 않으면 `InvalidCastException` 예외가 발생할 수 있습니다. 따라서 다운캐스팅을 수행하기 전에 `is` 연산자나 `as` 연산자를 사용하여 객체의 타입을 확인하는 것이 좋습니다.
즉, 다운캐스팅은 업캐스팅된 객체를 다시 원래의 파생 클래스 타입으로 사용해야 할 때 필요하며, 런타임에 객체의 실제 타입을 확인하고 사용해야 할 때 유용합니다. 하지만 다운캐스팅은 신중하게 사용해야 하며, 형변환이 유효한지 확인하는 것이 중요합니다.
2. 사용할 수 있는 멤버의 개수를 조절
이 블로그의 내용을 정리하면
일단 부모클래스의 멤버보다 파생클래스의 멤버가 더 많을 것이다(부모를 그대로 가져온뒤 확장하니까)
이때 부모타입의 참조변수 c는 부모의 멤버(itTurnOn,TurnOn,TurnOff)만 사용가능
반면 여태까지 했던 방식의 참조변수 n은 파생클래스의 모든 멤버 사용 가능
하지만 Dog dog = new Mammal(); 처럼
자식타입의 참조변수로 부모 타입의 인스턴스를 참조할 수 없다.
왜냐면 그럴 경우 실제 인스턴스의 멤버 개수보다 자식 타입의 참조변수가 사용할 수 있는 멤버 개수가 더 많기 때문
(참조로 부모타입을 가리키는데(부모의 멤버), 가지고 있는건 (부모+상속하면서 추가한 멤버)로 실제 인스턴스의 멤버 개수보다 자식타입의 참조변수가 사용할 수 있는 멤버 개수가 더 많기 때문
방금 이부분 잘이해한건지 모르겠다
형변환을 통해 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 개수를 조절
파생클래스는 명시적으로 부모클래스로 간주되기때문에(?) 형변환 생략가능.
생략가능이란 말보다 애초에 업캐스팅이 디폴트라고 해야되나?
확실히 알면 내용추가
이유는 위에서 말했듯이 사용할 수 있는 멤버의 개수가 더 많아지기 때문에 못하게 해놓는다
is와 as는 링크달아놓은 블로그, 책 참고
using System;
using static System.Console;
namespace thistiscsharp
{
class Mammal
{
public void Nurse()
{
Console.WriteLine("Nurse()");
}
}
class Dog : Mammal
{
public void Bark()
{
Console.WriteLine("Bark()");
}
}
class Cat : Mammal
{
public void Meow()
{
Console.WriteLine("Meow()");
}
}
class MainClass
{
public static void Main(string[] args)
{
Mammal mammal = new Dog(); //Dog을 Mammal로 업캐스팅
Dog dog; //new키워드와 Dog()생성자가 없으니 dog은 참조로써 객체가 있는 곳을 가리킬 뿐이다.
// dog자체에 메모리 할당이 되지 않고, dog은 null을 가진다
if(mammal is Dog) //mammal 이 Dog형식이면
//아까 Computer, NoteBook예제로 확인했듯이 mammal = dog이다.
{
dog = (Dog)mammal; //다운캐스팅.그렇기 때문에 (Dog)을 사용해 명시적으로 해줌
dog.Bark();
}
Mammal mammal2 = new Cat();
Cat cat = mammal2 as Cat;
if (cat != null)
cat.Meow();
Cat cat2 = mammal as Cat; //cat2는 cat1과달리 Mammal mammal2 = new Cat();을 쓰지않았기 때문에 Null
if (cat2 != null)
cat2.Meow();
else
Console.WriteLine("cat2 is not a Cat");
}
}
}