내일배움캠프 3주차 2일차 TIL - 클래스 상속 간 문제

백흰범·2024년 4월 30일
0
post-custom-banner

오늘 한 일

  • 스파르타 코딩클럽 (~4주차 과제)
  • 팀 프로젝트 전투 기능 구현하기

오늘은 팀 프로젝트와 개인 진도를 나아가면서 알게된 사실들을 적어보고자 한다.


문제 및 해결

내가 맞닥뜨린 문제

  • 부모 클래스와 자식 클래스가 있는데 자식 클래스에 생성자를 달고 부모 클래스 참조 변수에다가 자식 생성자를 사용해 인스턴스를 생성했는데 해당 변수에는 아무런 값(null)이 없는 현상.

  • 코드 예시

   class Parent
   {
       public string Name { get; private set; } 

       public void MyNameIs()
       {
           Console.WriteLine($"내 이름은 {Name}이야!");
       }
   }

   class Child : Parent
   {
       public string Name { get; private set; }

       public Child(string _name)
       {
           Name = _name;
       }
   }

   static void Main(string[] args)
   {
       Parent child = new Child("Rtan");

       child.MyNameIs();
   }
  • 의도치 않은 출력
    • "Rtan"이 출력되지 않았다.
    • 분명 Name이 들어간 건 같지만, Name을 꺼내지 않았다.

원인 분석

  • 아마 내가 코딩클럽 강의 부분 중 virtual과 override 내용을 들은 적이 있었는데, 부모 클래스 참조 변수로 자식 클래스의 인스턴스에 접근을 시도하려고 하면 부모 클래스에 있는 필드와 메서드부터 사용한다는 얘기를 들은 것 같다. 그래서 부모의 필드 Name(Program.Parent)의 Null을 꺼내서 저와 같이 출력된 것 같은 모양이다. (강의 내용에서는 아마 virtual override를 활용해서 자식 필드를 먼저 사용할 수 있도록 설정해놓았던 걸로 들었다.)

! 원인을 분석하면서 알게되는 점

  • 상속 받은 자식 클래스의 인스턴스를 생성하면 자식의 필드만 생성되는 것이 아니라 상속받은 모든 클래스들의 필드들도 함께 딸려온 채로 생성된다!
    • 사진 예시 1 [위] (최상위 클래스 참조 변수로 참조했을 때) 사진 예시 2 [밑] (해당 클래스 참조 변수로 참조했을 때)
    • 보면 child 인스턴스에 부모, 할아버지, 할아할아버지의 필드까지 생성이 되어 있다; ( 그로 인해서 상속 간 같은 이름의 필드가 존재하게 되면 호출했을 때 원하는 입력이 나오지 않을 가능성이 큰 것이다! )

문제 해결법

1. 자식에 있는 필드를 없애서 부모의 필드가 가려지지 않게 한다. (생성자 때문에 protected를 해줘야한다.)

-> 해결된 사진

  • 위와 같이 보면 상속 간 필드가 하나 밖에 없기에 잘못 가져올 걱정을 하지 않아도 된다!

2. virtual override를 활용하여 자식의 필드부터 꺼내게 만든다. (접근 제한자에 유의하자!)

-> 해결된 사진

  • 위 로컬을 보면은 비교를 위해 override 되지 않은 Gold를 추가했다. virtual 필드는 자식 필드에 override된 필드가 존재할 경우 부모가 따로 필드를 가지지 않고 자식의 필드만을 저장하고 있는 모습을 볼 수 있다.

3. 다운캐스팅을 활용

  • 다운 캐스팅 사용법
Parent parentInstace = new Child();
Child childInstance = parentInstance;	// 만약에 자식 클래스의 인스턴스를 참조하고 있지 않다면 에러가 발생한다!
  • 다운캐스팅을 활용하면 우선 순위가 바뀌어서 자식의 필드부터 꺼내오게 된다. (하지만 잘못된 예시와 함께 보여주겠다.)
    -> 잘못된 예시
  • 해당 출력이 잘못된 이유는 Child에는 MyNameIs 메서드가 없기 때문에 무조건 Parent의 메서드를 사용하게 되고, 그로 인해 Parent 클래스에서 자기 자신의 필드를 활용해서 메서드에 사용된 모습이다.

-> 잘 사용된 예시

  • 자식에 동일한 메서드를 넣어주면 의도된 출력이 나온다.

이 문제를 해결하고자 할 때는 DownCasting은 적절하지는 않는 것 같다...




그 외에 알게된 점

virtual override를 사용할 때 걸릴 수 있는 오류

! 가상 메서드가 private이면 재정의할 적절한 메서드를 찾을 수 없다. (CS0115)

public virtual string Name { get; private set; } -> public override string Name { get; private set; }

! 상속된 멤버를 재정의할 때 액세스 한정자를 변경할 수 없다. (CS0507)

public virtual string Name { get; set; }  ->  public override string Name { get; protected set; }

base()

  • 자식 생성자를 작성할 때 옆에 : base()를 활용하면 부모의 생성자를 불러와서 실행 후에 자식 생성자를 실행한다.
 class Parent
 {
     public string Name { get; protected set; }
     public int Gold { get; protected set; }

     public Parent()
     {
         Gold = 1000;
     }
 }

 class Child : Parent
 {
     public Child(string _name) : base()	// base()
     {
         Name = _name;
     }
 }

// 만들고 나서 실행!
     Child child = new Child("Rtan");

     Console.WriteLine(child.Name);
     Console.WriteLine(child.Gold);
  • 출력 결과

  • 정확히 순서상 부모 생성자부터 호출 후 자식 생성자를 호출한다는 것은 아래의 사진을 보면 알 수 있다.
  • 위 사진에서 알 수 있듯이 부모에도 Name을 할당하는 코드가 있지만, 순서상 뒤인 child의 생성자가 Name을 새로 덮어서 선언하게 된 모습이다.

디버그 과정

  • F11을 통해서 들어가면 base() 부분부터 들어가려는 모습을 볼 수 있다.
  • 다시 F11을 통해서 들어가면 해당하는 생성자를 찾아서(매개변수가 기준이다!) 실행한다.
    (보면 Gold에 1000과 Name에는 Sparta가 할당된 모습이다.)
  • 하지만 그 다음 자식 생성자로 인해서 Name이 Rtan으로 다시 할당된 모습이다.

위와 같이 생성자에 base()를 붙여서 인스턴스화할 때 부모 생성자와 자식 생성자를 세트로 호출할 수 있다!




공부를 하게되면서 느낀점

역시 초기 TIL은 프로그래밍 언어의 기반을 다져가는 게 맞는 것 같다. 물론 처음 배우는 사람에서 언어의 기반을 배워본 적 없는 사람에게까지는 무엇이 언어의 기초이고 기반인지 알 수가 없어 많이 방황할 수 있지만, 그런 방황을 하면서 기반이 뭔지 알아가는 과정도 중요한 학습 과정이라고 생각한다.

profile
게임 개발 꿈나무
post-custom-banner

0개의 댓글