ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 인터페이스와 열거형
    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
Designed by Tistory.