본문 바로가기

Software Engineering/Computer Science

[C#] 클래스, 직렬화, 메서드 수식자

이번 주차에 진행한 개인프로젝트를 돌아보며 쓰는 회고록 겸 추가로 궁금했던 사항들에 대해 정리하는 글이다. 

 

목   차

     


     

    GPT가 알려주는 C# 코너

    상속, 추상클래스, 인터페이스 각각을 사용할 상황.

    지난 금요일(24.04.26)에 튜터님한테 배운 내용을 생각해보니, 내가 아직 클래스들에 대한 이해도가 부족한 것을 느끼고 이에 대해 다양한 예를 들어 물었다. 

    #일반 부모클래스

    ex) 사람 클래스의 자식 클래스로 학생 클래스, 직장인 클래스가 있을 수 있지만 둘 모두에 속하지 않는 사람도 존재해야 할 때.

    • 공통의 기능이 있는 경우: 부모 클래스의 메서드/속성을 자식이 이어받게.
    • 확장성이 필요할 때 
    • 구체적 관계 표현

    #추상 클래스

    ex) 사람 클래스를 직접적으로 사용하지 않고 만약 현재 만들어진 클래스 중 내가 만드려는게 없다면 새로 만들어서(백수 클래스) 사용하면 했지 직접 끌어올 일은 없을 때.

    • 공통된 기본 기능 제공
    • 확장성 관리
    • 상태 관리(필드/속성)
    • 강제성을 위해서! (feat.강동욱 튜터님.)

    #인터페이스

    ex) 사람을 이루는 특징들(MBTI - 성격) 을 드래곤볼 해서 사용할 때.

    • 구현의 강제
    • 유연성이 중요할 때
    • 서로 다른 클래스에 동일한 기능을 제공하고자 할 때
    • 메서드 사양 정의
    • 다중 구현 지원
    • 계약 기반 프로그래밍

    #추상 클래스의 메서드

    • 일반 메서드
    정적 메서드일 때.(인스턴스에 속하지 않으므로 오버라이드 불가.)
    파이널 메서드일 때. (sealed 키워드를 통해 override를 막을 수 있음.)
        이미 오버라이드된 메서드일 때만 sealed 사용 가능. 재 오버라이드 방지용.
    기본 구현 메서드일 때. (모든 파생클래스에서 동일하게 동작해야만 하는 메서드.)
    • 가상 메서드
    기본 동작이 필요하되 필요에 따라 파생에서 바꿀 필요가 있을 때.
    • 추상 메서드
    모든 파생 클래스에서 반드시 제공해야 하는 기능을 정의할 때.(이때 상세 동작은 구현하지 않음.)

    직렬화 & 역직렬화

    #프로퍼티에 관하여

    • 변수는 private, 프로퍼티는 public일 때: 가능.
    • 프로퍼티 자체는 public이지만, getter만 public이고 setter은 private일 때:
      • System.Text.Json: 불가(저장은 되지만 불러올 때 초기화 불가.)
      • Newtonsoft.Json: 가능.

    오버헤드

    일단은 알던 단어지만, 잠깐 안봤다고 살짝 잊어서 재정리한다.

    1. 시간 오버헤드: 특정 작업을 수행하기 위해 추가로 소모되는 시간. 

        ex) if (a == b && b == c) 를 연산할 때 a==b와 b==c을 먼저 연산해야 그 다음 작업을 할 수 있으므로 &&은 잠깐 기다리는 느낌? (부적절한 예시일 수 있음(제대로 설명하기는 귀찮+시간소모->나중에?). 포인트는 "기다려야 한다")

     

    2. 공간 오버헤드: 프로그램 실행에 필요한 추가 메모리 사용량.

        ex) 캐싱 레이어, 런타임 관리 구조 등.

    3. 복잡성 오버헤드: 소프트웨어의 복잡성 증가로 인해 유지보수와 확장이 어려워지는 경우.

    업캐스팅 & 다운캐스팅

    #Upcasting

    자식 클래스의 인스턴스를 부모 클래스의 타입으로 캐스팅하는 것. 

    항상 안전하며, 자동으로 수행됨.

    class BaseClass {}
    class DerivedClass : BaseClass {}
    
    DerivedClass derived = new DerivedClass();
    BaseClass base = derived; // 상향 캐스팅, 명시적 캐스팅은 필요하지 않음

    #Downcasting

    부모 클래스의 인스턴스를 자식 클래스의 타입으로 캐스팅하는 것.

     항상 안전하지는 않기에 명시적 캐스팅이 필요함.

    잘못 사용할 경우 런타임 오류가 발생할 수 있음.

    기본 클래스 타입의 참조가 실제로 파생 클래스 타입의 인스턴스를 가리키고 있을 때만 안전하게 수행됨.

    BaseClass baseReference = new DerivedClass(); // 상향 캐스팅
    DerivedClass derivedReference = (DerivedClass)baseReference; // 하향 캐스팅, 성공적
    
    derivedReference.SpecialMethod(); // 파생 클래스의 특별 메서드 호출

     

    만약 다른 타입의 객체를 하향캐스팅하려 한다면 InvalidCastException 발생함.

    static void Main()
        {
            BaseClass baseReference = new AnotherClass(); // 상향 캐스팅, AnotherClass 인스턴스를 BaseClass 타입으로 캐스팅
            try
            {
                DerivedClass derivedReference = (DerivedClass)baseReference; // 시도하는 하향 캐스팅, 실패
                derivedReference.SpecialMethod();
            }
            catch (InvalidCastException ex)
            {
                Console.WriteLine("InvalidCastException caught: " + ex.Message);
            }
        }

     

    언제 다운캐스팅을 사용해야만 하는지에 대한 예제도 준비했다.

    public interface IAnimal {
        void Eat();
    }
    
    public class Dog : IAnimal {
        public void Eat() {
            Console.WriteLine("Dog eats");
        }
        public void Bark() {
            Console.WriteLine("Dog barks");
        }
    }
    
    public void FeedAnimal(IAnimal animal) {
        animal.Eat();
        if (animal is Dog dog) {
            dog.Bark(); // 특정 상황에서 Dog의 추가 기능을 사용
        }
    }

    수식자(modifier)

    키워드로는 알지만 그것들을 묶어 부르는 분류를 잘 몰라서 재정리한다.

    #접근 수식자

    • public
    • private
    • protected
    • internal

    #메서드 수식자

    • static
    • virtual
    • sealed
    • override
    • abstract

    #기타 수식자

    • readonly(초기화 이후 쓰기 불가)
    • volatile(몰?루)
    • const(상수)

     

    마무리

    다음부터는 이번처럼 GPT에 질문했던 것들도 꼬박꼬박 TIL/WIL로써 작성하려 한다.