-
인터페이스와 열거형C# 2023. 8. 24. 19:27
I. 다중 상속
: C# 에서는 이하의 이유로 클래스 간의 다중상속을 지원하지 않는다.
죽음의 다이아몬드 (Diamond Problem)
: 왼쪽 그림과 같은 상속관계는 오른쪽 상속관계와도 같다.
: D 클래스에서 A 클래스의 메서드를 사용해야 할 때, 이름의 충돌이 일어날 수 있다.
A를 상속받은 B의 것을 사용해야하는가? A를 상속받은 C의 것을 사용해야하는가?
: 또한 다음과 같이 B와 C가 메서드 Method1()을 오버라이드 한다고 하면,
class A { } class B : A { public virtual void Method1() { Console.WriteLine("You are groot"); } } class C : A { public virtual void Method1() { Console.WriteLine("We are groot"); } } class D : B, C // 코드상으로는 불가능함. 가정대로, B와 C를 다중상속한다 가정. { public override void Method1() { Console.WriteLine("They are groot"); } }
: 이 때, 클래스 D의 Method1은 누구를 오버라이드 해야하는가? B? C? 에 대한 문제가 생긴다.
설계의 복잡성 증가
: 어떤 클래스건 서로 정해지지 않은 수의 다른 클래스를 부모로 삼거나 자식으로 삼을 수 있게 된다면, 클래스 간의 관계가 복잡해진다.
이름 충돌과 충돌 해결의 어려움.
: 여러 부모 클래스로부터 상속받은 멤버들의 이름이 충돌할 수 있다.
II. 인터페이스
: 클래스가 구현해야 하는 멤버들을 정의한 것으로 클래스에 대한 제약 조건을 명시하는 것.
: 클래스가 인터페이스를 구현할 경우, 모든 인터페이스 멤버를 구현해야한다.
: 인터페이스는 다중상속이 가능함.
다른 클래스에서 동일한 인터페이스를 구현하는 것으로 동일한 메서드를 공유할 수 있다.
: 또한, 각 클래스마다 메서드를 다르게 설계할 수 있다.
interface IMovable { public void Move(int x, int y) {} } public class Player : IMovable { public void Move(int x, int y) {} } public class Enemy : IMovable { public void Move(int x, int y) {} } static void Main(string[] args) { IMovable movableObject1 = new Player(); // IMovable을 통하여 참조를 함. IMovable movableObject2 = new Enemy(); movableObject1.Move(1, 2); movableObject2.Move(3, 7); }
: Player와 Enemy의 Move 서로 다른 클래스의 메서드지만 인터페이스 IMovable을 통해 각자의 메서드를 참조함.
=> Move라는 공통된 기능(각기 같은 이름의 메서드를 보유)을 정의하고 구현할 수 있음.
인터페이스는 메서드의 파라미터로 사용 가능하다.
: 메서드가 메서드의 파라미터로 사용 가능한 것 처럼, 메서드를 참조하는 인터페이스 또한 가능하다.
: 인터페이스를 상속하여 구현한 클래스(인터페이스의 추상메서드)의 인스턴스가 초기화되면, 인스턴스는 상속받는 인터페이스를 구현한 객체로도 간주됨.
public interface IUsable { void Use(); } public class PotionItem : IUsable { public string Name { get; set; } public void Use() { Console.WriteLine("아이템 {0}을 사용했습니다.", Name); // 아이템의 남은 갯수 감소 로직 } } public class BuffItem : IUsable { public string Name { get; set; } public void Use() { Console.WriteLine("버프 {0}을 사용했습니다", Name); // 버프 적용 로직 } } public class Player { public void UseItem(IUsable item) // 매개변수로 IUsable 인터페이스를 구현한 객체(PotionItem에서 구현했든, BuffItem에서 구현했든) 사용 가능. { item.Use(); } } static void Main(string[] args) { Player player = new Player(); PotionItem item = new PotionItem { Name = "Health Potion" }; BuffItem buff = new BuffItem { Name = "attack increase" }; player.UseItem(item); // item은 PotionItem의 객체이자 IUsable 인터페이스를 구현한 객체임. player.UseItem(buff); // buff는 BuffItem의 객체이자 IUsable 인터페이스를 구현한 객체임. }
인터페이스는 다중상속을 지원한다.
: 하나의 클래스는 복수의 인터페이스를 상속받아 구현할 수 있다.
public interface IItemPickable { void PickUp(); } public interface IDroppable { void Drop(); } public class Item : IItemPickable, IDroppable // 2개의 인터페이스 상속 { public string Name { get; set; } public void PickUp() // IItemPickable 구현 { Console.WriteLine("아이템 {0}을 주웠습니다.", Name); } public void Drop() // IDroppable 구현 { Console.WriteLine("아이템 {0}을 버렸습니다.", Name); } } public class Player { public void InteractWithItem(IItemPickable item) { item.PickUp(); } public void DropItem(IDroppable item) { item.Drop(); } } static void Main(string[] args) { Player player = new Player(); Item item = new Item { Name = "Sword" }; player.InteractWithItem(item); player.DropItem(item); }
추상 클래스 (Abstract Class)
: 직접적으로 인스턴스를 생성할 수 없는 클래스
: 주로 상속을 위한 베이스 클래스
: abstract 키워드를 사용하며 선언
abstract class Shape // 추상 클래스 사용 { public abstract void Draw(); // 추상 메서드 선언 } class Circle : Shape { public override void Draw() // 추상 메서드 오버라이딩 { Console.WriteLine("Drawing a circle"); } } class Square : Shape { public override void Draw() // 추상 메서드 오버라이딩 { Console.WriteLine("Drawing a square"); } } class Triangle : Shape { public override void Draw() // 추상 메서드 오버라이딩 { Console.WriteLine("Drawing a triangle"); } }
인터페이스 vs 추상클래스
: 상속을 받아야 하는 경우에는 추상 클래스를, 기능에 대한 호환성을 높이고 싶다면 인터페이스를 사용함.
: 인터페이스는 상속관계에 놓이지 않은 클래스간에도 사용 가능함.
: 인터페이스는 다중 상속이 가능함.
III. 열거형 (Enumeration)
: 서로 연관된 상수들을 의미 있는 이름을 사용하여나타내는 데이터 형식
: 스위치 문과의 호환성이 좋다.
: 열거형의 각 상수는 정수 값으로 지정된다.
열거형 정의
enum "enum 이름"
{
상수 이름 1 = 지정하고 싶은 정수값,
상수 이름 2,
상수 이름 3,
...
}public enum Month { January = 1, February, March, April, May, June, July, August, September, October, November, December }
열거형 사용
"enum 이름" "변수 이름" = "enum 이름"."상수 이름";
// 처리하는 함수 static void ProcessMonth(int month) { // if (month >= 1 && month <= 12) // (int)Month.Jan = 1 // (int)Month.Dec = 12 로 조건식의 상수대신 사용 가능. // 열거형을 상수대신 입력하는 것이 더 명확하게 의도를 전할 수 있음. if (month >= (int)Month.Jan && (int)Month.Dec) { Month selectedMonth = (Month)month; Console.WriteLine("선택한 월은 {0}입니다.", selectedMonth); } else { Console.WriteLine("올바른 월을 입력해주세요."); } } // 실행 예제 static void Main() { int userInput = 7; // 사용자 입력 예시 ProcessMonth(userInput); }
'C#' 카테고리의 다른 글
값형과 참조형 (0) 2023.08.24 예외 처리 (1) 2023.08.24 텍스트 던전 콘솔로 구현하기 - 작업중 학습(2) (0) 2023.08.23 스네이크 게임 만들기 (수정예정) (0) 2023.08.21 제네릭 (0) 2023.08.18