# abstract 필요 예시
C++에서 추상클래스를 사용해야 하는 예시와 동일하다
C#에서 부모 클래스를 상속받은 자식 클래스에서
부모 클래스의 virtual 함수를
override를 하지 않거나 (부모 클래스 함수를 그대로 사용하고 싶은 경우)
override 하여 사용하는 것이 일반적이다 (부모 클래스 함수와는 다른 구현부의 함수를 사용하고 싶은 경우)
하지만 만약,
부모 클래스는 함수의 "이름"만 정의하고,
구현은 자식 클래스에서만 가능하도록 강제하고 싶다면
abstract 키워드를 사용하면 된다
abstract 키워드가 함수에 붙으면,
해당 함수는 추상함수가 되고
해당 함수가 포함된 클래스 전체가 추상클래스가 돼버린다
따라서,
부모 클래스는 해당 함수의 구현(내용)을 가질 수 없으며,
자식 클래스는 반드시 override 해서 구현해야 하고
부모 클래스로 객체 생성은 불가능해진다
public abstract class Animal
{
// virtual: 부모가 기본 동작을 제공 (자식이 덮어써도 되고, 안 해도 됨)
public virtual void Eat()
{
Console.WriteLine("동물이 먹는다.");
}
// abstract: 부모는 정의만, 자식이 반드시 구현해야 함
public abstract void Speak();
}
public class Dog : Animal
{
// virtual 함수 override (선택사항)
public override void Eat()
{
Console.WriteLine("강아지가 사료를 먹는다.");
}
// abstract 함수 override (필수)
public override void Speak()
{
Console.WriteLine("멍멍!");
}
}
class Program
{
static void Main(string[] args)
{
Dog dog = new Dog();
dog.Eat(); // 강아지가 사료를 먹는다.
dog.Speak(); // 멍멍!
}
}
# abstract 키워드
- abstract 키워드를 클래스 앞에 붙이면?
해당 클래스를 추상클래스로 만든다
추상클래스는 객체 생성이 불가능하고 자식클래스만 생성이 가능하다
주의점은 해당 추상클래스에 abstract 키워드 붙은 함수가
하나도 없더라도 추상 클래스는 가능하다
- abstract 키워드를 함수 앞에 붙이면?
해당 함수를 추상함수로 만든다
추상함수는 선언만 가능하고 자식클래스에서 구현이 필요하다
주의점은 클래스 내부에 추상함수가 단 하나만 있더라도
해당 클래스는 추상클래스가 되고
abstract 키워드를 반드시 클래스 앞에 붙여줘야 오류가 발생하지 않는다
- C++과의 비교
C++에서 virtual 키워드와 '= 0'을 사용해
순수 가상 함수(Pure Virtual Function)를 만들게 되면,
해당 클래스는 추상 클래스가 된다
C#에서도 이런 개념으로
abstract 키워드를 클래스 멤버 함수에 붙이는 행위가
결국 순수가상함수를 만드는 개념인 것이다
그리고 C++에서 순수가상함수가 하나라도 클래스에 있으면 추상 클래스가 되는 것처럼
C#에서도 추상함수가 하나라도 클래스에 있으면 추상 클래스가 되며
abstract 키워드를 반드시 클래스에 작성해야 컴파일 오류가 발생하지 않는다는 것이다
# 다중상속
- C++의 다중상속
C++은 다중상속이 가능하지만 C#은 다중상속이 불가능하다
다중상속의 근본적인 문제는
"다이아몬드 문제 (누굴 써야 해?)"이다
아래 C++ 코드를 통해서 다중상속이 지원되는 경우의 해당 문제를 확인할 수 있다
// 최상위 클래스
class Animal
{
public:
void Speak()
{
cout << "동물이 소리 낸다" << endl;
}
};
// 중간 클래스 A
class Mammal : public Animal
{
};
// 중간 클래스 B
class Bird : public Animal
{
};
// 다중 상속
class Bat : public Mammal, public Bird
{
};
int main()
{
Bat bat;
// bat.Speak(); // 에러! 모호함
// 아래처럼 명시해야 함
bat.Mammal::Speak(); // 가능
bat.Bird::Speak(); // 가능
return 0;
}
- C#의 다중상속
위와 같은 문제를 해결하기 위해서 C#에서는 인터페이스 개념을 사용한다
인터페이스 개념은 아래와 같다
"인터페이스(ex. 멤버함수의 정의부)는 물려주지만 구현부는 물려주지 않으면 되지 않나?"
interface를 작성하는 방법은 아래와 같고
접근 지정자가 불필요하다
또한, interface는 다중 상속이 가능하고
interface를 상속받으면 반드시 해당 함수 구현을 해주어야 한다
interface IFlyable
{
void Fly(); // 접근 지정자 불필요, 정의만
}
- Inteface를 굳이?
1. 느슨한 결합으로 유지보수가 쉬워진다
기존 컴포넌트 방식:
public class Player
{
public DamageComponent damage;
public HealComponent heal;
public void TakeAction(string type)
{
if (type == "Damage")
damage.ApplyDamage(10);
else if (type == "Heal")
heal.ApplyHeal(5);
}
}
public class DamageComponent { public void ApplyDamage(int amount) { } }
public class HealComponent { public void ApplyHeal(int amount) { } }
인터페이스 방식:
public interface IAction
{
void Execute(int amount);
}
public class Player
{
public Dictionary<string, IAction> actions = new();
public void TakeAction(string type, int amount)
{
if (actions.ContainsKey(type))
actions[type].Execute(amount);
}
}
public class DamageAction : IAction { public void Execute(int amount) { } }
public class HealAction : IAction { public void Execute(int amount) { } }
2. 인터페이스로 객체 생성 및 관리가 가능
interface IMovable
{
void Move();
}
interface IAttackable
{
void Attack();
}
class Player : IMovable, IAttackable
{
public void Move()
{
Console.WriteLine("플레이어가 이동 중...");
}
public void Attack()
{
Console.WriteLine("플레이어가 공격 중...");
}
}
class Monster : IAttackable
{
public void Attack()
{
Console.WriteLine("몬스터가 공격 중...");
}
}
class Program
{
static void Main()
{
IMovable movable = new Player();
IAttackable attackable = new Monster();
movable.Move();
attackable.Attack();
}
}
- 정리
컴포넌트:
자신의 내부 상태를 추적하거나 자신만 쓰는 동작을 처리하는 경우에 사용
인터페이스:
외부 시스템이 특정 클래스에게 어떤 "기능"을 요청해야 하거나
동일한 역할을 다양한 클래스들이 수행하는 경우에 사용
- 정리 2
외부 시스템에서 해당 기능을 쉽게 ‘찾아 호출’하려고 할 때만 인터페이스를 쓰면 된다
내부에서만 쓰는 상태/로직: 컴포넌트
외부에서 공통된 요청이 들어오는 경우: 인터페이스
'C# > [루키스] 실전 문법' 카테고리의 다른 글
| [C# 섹션 8] Exception (2) | 2025.06.26 |
|---|---|
| [C# 섹션 8] Lambda (0) | 2025.06.25 |
| [C# 섹션 8] Event (0) | 2025.06.25 |
| [C# 섹션 8] Delegate (0) | 2025.06.25 |
| [C# 섹션 8] Property (0) | 2025.06.24 |