Notice
Recent Posts
Recent Comments
Link
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

I'm FanJae.

[20260508] C# (Upcasting, Downcasting, is / as) 본문

Unity/Unity 초격차캠프

[20260508] C# (Upcasting, Downcasting, is / as)

FanJae 2026. 5. 8. 18:14

1. 캐스팅(Casting)

(1) 정의

- Casting이란 객체나 값을 다른 타입으로 변환해서 바라보는 것을 의미한다.

- 상속 관계에서는 하나의 객체를 부모 타입 또는 자식 타입 관점으로 다룰 수 있게 해준다.


2. 업캐스팅(Upcasting)

(1) 정의

- Upcasting은 자식 객체를 부모 타입으로 참조하는 것을 의미한다.

 
Dog dog = new Dog();
Animal animal = dog;

- 여기서 animal 변수는 Animal 타입이지만, 자식 객체인 dog 를 참조하고 있다.

- 따라서, Upcasting이 발생한 것이다.


(2) 특징

- Upcasting은 자식 클래스 객체를 부모 클래스 타입으로 참조하는 상향 형변환이다.

- 명시적인 캐스팅을 하지 않아도 자동으로 변환이 가능하다.

- 부모 클래스 타입 관점으로 보는 것이다.

- 즉, 변수의 타입이 부모 클래스이기 때문에, 코드 상에서는 부모 클래스에 정의된 기능만 사용할 수 있다.

- 실제 객체는 여전히 자식 클래스 객체이다.

- 즉, 업캐스팅을 하더라도 객체 자체가 부모 클래스 객체로 바뀌는 것은 아니다.


(3) Upcasting이 필요한 이유

- 여러 자식 객체를 부모 타입 하나로 묶어서 다룰 수 있기 때문이다.

① Upcasting이 필요한 예제 (다형성 구현)

abstract class Animal
{
	protected string Name;
	public abstract void MakeSound();
}
class Dog : Animal
{
	public override void MakeSound()
	{
		Console.WriteLine($"{Name}이 멍멍! 하고 짖습니다");
	}
}
class Cat : Animal
{
	public override void MakeSound()
	{
		Console.WriteLine($"{Name}이 야옹~~~! 하고 웁니다");
	}
}
Animal[] animals = { new Dog("개"), new Cat("고양이") };

animals[0].MakeSound();
animals[1].MakeSound();

- 이렇게 부모 타입을 이용해서 자식 객체에 접근할 수 있다.

- 이전 내용부터 몇 번씩 강조되고 있는 다형성을 구현할 수 있게 되는 것이다.


(4) 주의점

- 업캐스팅 상태에서는 부모 클래스에 정의된 멤버만 접근 가능하다.

- 예를들어, Animal 에는 없고, Dog에만 존재하는 Bite() 메소드가 있다고 가정한다.

 

① Upcasting 상황에서 접근이 불가능한 경우

class Dog : Animal
{
	public override void MakeSound()
	{
		Console.WriteLine($"{Name}이 멍멍! 하고 짖습니다");
	}
	public void Bite()
	{
	  Console.WirteLine($"{Name}이 당신을 물었습니다.");
	}
}
Animal animal = new Dog("개");
animal.MakeSound(); // 가능
animal.Bite() // 불가능

업캐스팅 상태에서는 컴파일 타임 기준으로 변수의 타입이 부모 클래스 타입이다.

- 따라서 animal은 부모 클래스에 정의된 멤버(MakeSound())에만 접근할 수 있다.

- 하지만 virtual / override 메서드는 런타임에 실제 객체 타입을 기준으로 실행된다.


3. 다운캐스팅(Downcasting)

(1) 정의

- Downcasting은 부모 타입으로 참조 중인 객체를 다시 자식 타입으로 변환하는 것이다.

Animal animal = new Dog();
Dog dog = (Dog)animal;

- 여기서 animal은 Dog 객체를 참조하고 있지만, 실제 타입은 Animal 이다.

- 다음 문장에서 명시적 캐스팅을 하여, Dog 타입으로 변환하였다.

- 따라서, Downcasting이 발생한 것이다.


(2) 특징

- Downcasting은 부모 타입을 자식 타입으로 변환하는 하향 형변환이다.

- 명시적인 형변환이 필요하다.

- 형변환의 실패 가능성이 존재한다.

- 잘못된 타입으로 캐스팅하면 런타임 예외가 발생한다.


(3) 필요한 이유

- 부모 타입으로는 자식 클래스만 가진 기능에 접근할 수 없기 때문에 필요하다.

 

① Downcasting이 필요한 예제

class Dog : Animal
{
	public override void MakeSound()
	{
		Console.WriteLine($"{Name}이 멍멍! 하고 짖습니다");
	}
	public void Bite()
	{
	  Console.WirteLine($"{Name}이 당신을 물었습니다.");
	}
}
Animal animal = new Dog("개");
animal.MakeSound(); // 가능
animal.Bite() // 불가능

Dog dog = (Dog)animal;
dog.Bite(); // 가능

- 이렇게 부모 타입을 자식 타입으로 Downcasting하여 자식 클래스만 가진 기능에 접근 가능하다.

- Downcasting은 필요한 경우에만 사용하고, 가능하면 다형성을 활용해 피해야 한다.

 

② 그러면 어떻게 나눠야 하는가?

- 개의 물기(Bite)를 ‘공통 기능’으로 묶을 수 있는지 생각해야 한다.

- 개의 물기를 ‘공격’이라고 생각해보면, 설계 관점에서 4가지로 나뉠 수 있다.

- Case 1. 모든 동물에 대해서 똑같은 동작을 한다.
    → 부모 클래스에 구현하여 상속 형태로 처리한다.
- Case 2. 똑같은 동작을 할 수 있지만, 일부 동물은 다른 동작을 한다.
    → virtual를 이용한 구현
- Case 3. 모든 동물이 서로 다른 동작을 해야 한다.
    → abstract를 이용한 구현
- Case 4. 어떤 동물은 공격을 할 수 있지만, 어떤 동물은 공격을 할 수 없다.
    → interface를 이용한 구현

 

(1) Case 1. 모든 동물에 대해서 똑같은 동작을 하는 경우

class Animal
{
		protected string Name;
		public void Attack()
		{
				Console.WriteLine($"{Name}이 공격한다."); 
		}
}
class Dog : Animal
{
}

- 모든 동물이 동일한 동작을 한다면 상속으로 처리해서 부모 클래스에 구현해야 한다.

 

(2) Case 2. 기본은 똑같지만, 일부 동물만 다른 동작인 경우

- virtual 을 사용한 다형성 구현

- 모든 동물에게 기본 공격 개념(기본 구현)을 두고 싶다면, virtual 형태로 처리한다.

- 이렇게 처리하면, 자식 클래스(각 동물)은 필요하면 override 하여 바꿀 수 있다.

class Animal
{
	protected string Name;
	public virtual void Attack()
	{
		Console.WriteLine($"{Name}이 공격한다."); 
	}
}
class Dog : Animal
{
	public virtual void Attack()
	{
		Console.WriteLine($"{Name}이 이빨로 물어 뜯습니다.");
	}
}

 

(3) Case 3. 모든 동물이 서로 다른 동작을 해야 하는 경우

- abstract 을 사용한 다형성 구현 

- 동물에게 기본 공격 개념을 두고 싶지 않다면, abstract 형태로 처리한다.

- 이렇게 처리하면, 자식 클래스(각 동물)은 반드시 override 해야 한다.

abstract class Animal
{
	protected string Name;
	public abstract void Attack();
}
class Dog : Animal
{
	public override void Attack()
	{
		Console.WriteLine($"{Name}이 이빨로 물어 뜯습니다.");
	}
}

 

(4) Case 4. 모든 동물이 공격을 하지 않는 경우

- 인터페이스를 이용해서 IAttackable . 즉, 공격을 구현하는 형태로 만든다.

interface IAttackable
{
	void Attack();
}
class Dog : Animal, IAttackble
{
	public void Attack()
	{
		Console.WriteLine($"{Name}이 이빨로 물어 뜯습니다.");
	}
}

(4) 주의점

- Downcasting은 항상 성공하는 변환이 아니다.

- 부모 타입 변수라고 해서 모든 자식 타입으로 변환할 수 있는 것은 아니다.

Animal animal = new Dog();

Dog dog = (Dog)animal; // 가능
Cat cat = (Cat)animal; // 불가능

- 실제 객체가 해당 자식 타입이거나, 해당 자식 타입으로 변환 가능한 경우에만 성공한다.

- 잘못된 타입으로 명시적 캐스팅을 하면 런타임 예외가 발생한다.

- 따라서 안전한 캐스팅이 필요하고, 가능하면 다운 캐스팅 상황을 만들지 않는게 좋다.


4. 안전한 Downcasting

(1) is

- is 는 객체가 특정 타입으로 변환 가능한지 확인한다. 가능하면 바로 변수로 사용할 수 있다.

- 변환이 가능하면, true 실패하면, false 를 반환한다.

- ‘패턴 매칭’과 함께 사용하면, 타입 확인과 동시에 변환된 변수를 바로 사용할 수 있다.

Animal animal = new Dog();

if (animal is Dog dog)
{
    dog.Bite();
}

- 위에서 Dog dog 가 패턴 매칭이다. 객체가 특정 타입이나 조건에 맞는지 검사한다.

- 조건에 맞으면 그 값을 해당 타입의 변수로 사용할 수 있게 해준다.

(2) as

- as 는 캐스팅에 성공하면 해당 타입 참조를 반환한다. 실패하면 예외 대신 null 을 반환한다.

Animal animal = new Dog();

Dog dog = animal as Dog;

if (dog != null)
{
    dog.Bite();
}

(3) is와 as의 차이

- is 는 타입 확인과 변환을 한 번에 처리할 때 사용하기 좋다.

- as 는 변환을 시도한 뒤, 결과가 null인지 확인하는 방식이다.

- 다운캐스팅이 필요한 상황이면 is 를 이용한 패턴 매칭 방식이 직관적이다.

- as는 변환 결과를 변수에 저장해두고, 이후에도 계속 사용하고 싶을 때 사용한다.

 
Comments