2025.01.30 (목)

윤혜진·2025년 1월 30일
0

TIL

목록 보기
15/38

📍오늘의 학습 키워드

  • 4주차 강의 수강
    1. 인터페이스와 열거형
      • 다중 상속을 사용하지 않는 이유
      • 인터페이스를 사용하는 이유
      • 인터페이스 (Interface)
      • 열거형 (Enums)
    2. 예외 처리 및 값형과 참조형
      • try-catch
      • 값형과 참조형
      • 박싱과 언박싱
    3. 델리게이트, 람다 및 LINQ
      • 델리게이트
      • 이벤트
      • 람다 (Lambda)
    4. 고급 자료형 및 기능
      • Nullable 형
      • 문자열 빌더 (StringBuilder)

📍학습 내용

  • 인터페이스와 열거형
    • 다중 상속을 사용하지 않는 이유 :
      1. 다이아몬드 문제:
        • 다중 상속을 허용하면 한 클래스가 두 개 이상의 부모 클래스로부터 동일한 멤버를 상속받을 수 있음.
        • 이 경우 어떤 부모클래스의 멤버를 사용해야 하는지 모호해지는 문제가 발생할 수 있다.
      2. 설계의 복잡성 증가:
        • 클래스 간의 관계가 복잡해진다.
      3. 이름 충돌과 충돌 해결의 어려움
        • 다중 상속을 허용하면 여러 부모 클래스로부터 상속받은 멤버들의 이름이 충돌할 수 있음.
        • 이는 코드의 복잡성을 증가시키고 오류 발생 가능성을 높인다.
      4. 설계의 일관성과 단순성 유지
        • C#은 단일 상속을 통해 설계의 일관성과 단순성을 유지하고자 함.
        • 또한 인터페이스를 사용하여 다중 상속이 필요한 경우에도 유사한 기능을 구현할 수 있음
    • 인터페이스를 사용하는 이유 :
      • 코드의 재사용성
        • 인터페이스를 통해 다양한 클래스가 동일한 동작을 수행할 수 있으므로 코드의 재사용성이 향상됨
      • 다중 상속 제공
        • C#은 단일 상속만 제공하지만, 인터페이스는 다중 상속을 지원함.
        • 클래스가 여러 인터페이스를 구현함으로써 여러 개의 기능을 조합할 수 있음
      • 유연한 설계
        • 인터페이스를 사용하면 클래스와 인터페이스 간에 느슨한 결합을 형성할 수 있음
        • 클래스는 인터페이스를 구현만 하면 되므로, 클래스의 내부 구현에 대한 변경 없이 인터페이스의 동작을 변경하거나 새로운 인터페이스를 추가할 수 있음
    • 인터페이스 (Interface)
      • 우리는 인터페이스를 이미 사용하고 있음. (예시: USB)
      • 저 포트 하나로 다양한 기기들이 호환되어서 사용될 수 있도록 만들어짐 : 이것이 인터페이스의 기능
      • 인터페이스의 특징 :
        • 인터페이스란 클래스가 구현해야 하는 멤버들을 정의하는 것
        • 클래스의 일종 X / 클래스에 대한 제약 조건을 명시 O
        • 클래스가 인터페이스를 구현할 경우, 모든 인터페이스 멤버를 구현해야 함
        • 인터페이스는 다중 상속을 지원한다.
      • 인터페이스 및 멤버 정의하기
        • 인터페이스를 사용할 땐 이름 맨 앞에 대문자 I를 붙여줌
          interface IMyInterface
          {
              void Method1();
              int Method2(string str);
          }
      • 인터페이스 구현하기
        • 추상 클래스와 크게 다르지 않음
          class MyClass : IMyInterface
          {
              public void Method1()
              {
                  // 구현
              }
          
              public int Method2(string str)
              {
                  // 구현
                  return 0;
              }
          }
    • 인터페이스와 추상 클래스의 차이점 :
      특징인터페이스 (Interface)추상 클래스 (Abstract Class)
      목적여러 클래스에 공통된 동작을 강제하기 위함상속을 통해 기본 기능을 제공하고 확장하기 위함
      멤버 구성메서드, 속성, 이벤트(모두 구현 없이 선언만 가능, C# 8부터는 일부 구현 가능)필드, 생성자, 메서드(추상 & 일반 메서드 모두 가능)
      구현 여부메서드의 본문(구현부)을 가질 수 없음 (C# 8 이후 일부 가능)일반 메서드와 추상 메서드 모두 가능
      상속 관계다중 구현 가능 (여러 개의 인터페이스 사용 가능)단일 상속만 가능
      인스턴스 생성인스턴스 생성 불가인스턴스 생성 불가
      접근 제한자기본적으로 public (접근 제한자 지정 불가)public, protected, private 등 가능
      사용 예시여러 클래스에 동일한 동작을 강제할 때 (예: 동물들이 모두 "소리 내기")기본 기능을 제공하면서 세부 구현을 자식 클래스에 맡길 때 (예: 동물의 기본 행동 제공)

    • 인터페이스 vs 추상 클래스 언제 사용할까? :
      상황인터페이스 사용추상 클래스 사용
      여러 클래스가 공통 기능을 가져야 할 때✅ (예: IDisposable, IComparable)✅ (예: AnimalDog, Cat)
      기본 구현 없이 규칙만 강제할 때✅ (구현 없이 메서드 시그니처만 정의)❌ (추상 클래스는 일부 구현을 포함 가능)
      기본 기능(메서드, 필드)을 제공할 때❌ (메서드 구현 불가능)✅ (일반 메서드 구현 가능)
      다중 상속(다중 구현)이 필요할 때✅ (여러 인터페이스 구현 가능)❌ (클래스는 단일 상속만 가능)
      기존 클래스를 확장할 때❌ (인터페이스는 기본 구현이 없어서 확장 불리)✅ (추상 클래스는 기존 기능을 확장하기 좋음)

    • 열거형 (Enums)
      • 열거형을 사용하는 이유:
        1. 가독성:

          • 열거형을 사용하면 일련의 연관된 상수들을 명명할 수 있다.
          • 이를 통해 코드의 가독성이 향상되고, 상수를 사용할 때 실수로 잘못된 값을 할당하는 것을 방지할 수 있음.
        2. 자기 문서화:

          • 열거형은 의미 있는 이름을 사용하여 상수를 명명할 수 있다.
          • 이를 통해 코드의 가독성이 향상되고, 상수의 의미를 명확하게 설명할 수 있음.
        3. 스위치 문과의 호환성:

          • 열거형을 사용하면 스위치 문에서 다양한 상수 값에 대한 분기를 쉽게 작성할 수 있다.
          switch(enumValue)
          {
              case MyEnum.Value1:
                  // Value1에 대한 처리
                  break;
              case MyEnum.Value2:
                  // Value2에 대한 처리
                  break;
              case MyEnum.Value3:
                  // Value3에 대한 처리
                  break;
              default:
                  // 기본 처리
                  break;
      • 열거형 특징:
        • 열거형은 서로 관련된 상수들의 집합을 정의할 때 사용된다.
        • 열거형의 각 상수는 정수 값으로 지정된다.
      • 열거형 사용 예제 :
        // 열거형 정의 및 상수값 지정
        enum MyEnum
        {
            Value1 = 10,
            Value2,        //앞에 10을 지정했으므로 Value2는 자동으로 11이 된다.
            Value3 = 20
        }
        
        // 열거형 사용
        MyEnum myEnum = MyEnum.Value1;
        
        // 열거형 형변환
        int intValue = (int)MyEnum.Value1;  // 열거형 값을 정수로 변환
        MyEnum enumValue = (MyEnum)intValue;  // 정수를 열거형으로 변환
  • 예외 처리 및 값형과 참조형
    • try-catch
      try
      {
          // 예외가 발생할 수 있는 코드
      }
      catch (ExceptionType1 ex)
      {
          // ExceptionType1에 해당하는 예외 처리
      }
      catch (ExceptionType2 ex)
      {
          // ExceptionType2에 해당하는 예외 처리
      }
      finally
      {
          // 예외 발생 여부와 상관없이 항상 실행되는 코드
      }
      • try 블록 내에서 예외가 발생할 수 있는 코드를 작성하고, catch 블록에서 예외를 처리한다.
      • catch 블록은 위부터 순서대로 실행되며, 여러 개의 catch 블록을 사용하여 다양한 예외 타입을 처리할 수 있다.
        static void Main()
            {
                try
                {
                    Console.Write("숫자를 입력하세요: ");
                    string input = Console.ReadLine();
                    int number = int.Parse(input);  // 입력값이 숫자가 아니면 예외 발생!
                    int result = 100 / number;  // 0이면 예외 발생!
        
                    Console.WriteLine($"결과: {result}");
                }
                catch (FormatException)  // 잘못된 입력 처리
                {
                    Console.WriteLine("오류: 숫자를 입력해야 합니다!");
                }
                catch (DivideByZeroException)  // 0으로 나누는 경우 처리
                {
                    Console.WriteLine("오류: 0으로 나눌 수 없습니다!");
                }
                catch (Exception ex)  // 모든 예외를 처리하는 기본 catch (맨 마지막에 둠)
                {
                    Console.WriteLine($"알 수 없는 오류 발생: {ex.Message}");
                }
            }
    • 사용자 정의 예외
      • C#에서 제공하는 예외처리 객체 이외에 사용자 정의 예외를 만들려면, Exception 클래스를 상속받아 새로운 예외 클래스를 정의하면 된다.
                public class NegativeNumberException : Exception
                {
                    //생성자 : 이니셜라이저        
                    public NegativeNumberException(string message) : base(message)
                    {
                        //이니셜라이저는 생성자가 실행되기 전에 처리할 것들을 명시한다.
                        //base == Exception(부모클래스)
                        //즉 Exception의 생성자를 먼저 실행하고
                        //NegativeNumberException의 생성자를 실행한다.
                    }
                }
        
                static void Main(string[] args)
                {
                    try
                    {
                        int number = -10;
                        if (number < 0)
                        {
                            //throw: 오류를 일부러 일으킴
                            throw new NegativeNumberException("음수는 처리할 수 없습니다.");
                        }
                    }
                    catch (NegativeNumberException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("예외가 발생했습니다: " + ex.Message);
                    }
        
                }
    • if문과 try-catch의 차이점
      비교 항목if문 (예방적 검사)try-catch (예외 처리)
      오류 발생 방식예외가 발생하기 전에 조건을 확인해서 방지예외가 발생한 후 잡아서 처리
      사용 목적예상 가능한 오류를 방지할 때예상하지 못한 오류도 안전하게 처리할 때
      성능비교적 빠름 (예외가 없으면 코드 실행 속도 영향 없음)예외가 발생하면 성능 비용이 큼
      처리 방식조건문을 사용하여 직접 해결예외가 발생하면 자동으로 호출 스택을 따라감
      필수 여부단순한 오류 방지는 if문으로 충분파일, 네트워크, 데이터베이스 오류는 try-catch가 필수
      • if문을 사용해 예외처리를 해도 괜찮은 경우 :
        • 잘못된 입력 방지
          • if (age > 0) {} (나이는 0보다 커야 함)
        • 배열 범위 초과 방지
          • if (index >= 0 && index < array.Length) {}
        • 파일이 존재하는지 확인
          • if (File.Exists(path)) {}
      • try-catch문을 사용해야 하는 경우 :
        • 네트워크 오류 발생 가능
          • WebClient.DownloadString(url)
        • 파일이 갑자기 삭제됨
          • File.ReadAllText(path)
        • 숫자가 아닌 값 입력
          • int.Parse(input)
        • 데이터베이스 연결 문제
          • SqlConnection.Open()
  • 값형과 참조형
    • C#에서 변수가 데이터를 저장하는 방식을 나타낸다.
    • 값형 (Value Type) :
      • 값형은 변수에 값을 직접 저장
      • 변수가 실제 데이터를 보유하고 있으며, 해당 변수를 다른 변수에 할당하거나 전달할 때는 값이 복사된다.
      • 값형 변수의 수정은 해당 변수의 값만 변경하므로 다른 변수에 영향을 주지 않는다.
      • char, int, float, double, bool 등의 기본 데이터 타입들이 값형에 해당.
    • 참조형 (Reference Type) :
      • 참조형은 변수가 데이터에 대한 참조(메모리 주소)를 저장
      • 변수가 실제 데이터를 가리키는 참조를 갖고 있으며, 해당 변수를 다른 변수에 할당하거나 전달할 때는 참조가 복사된다.
      • 참조형 변수의 수정은 동일한 데이터를 가리키고 있는 다른 변수에 영향을 줄 수 있다.
      • String, 클래스, 배열, 인터페이스 등이 참조형에 해당.
  • 박싱과 언박싱
    • 값형과 참조형 사이의 변환을 의미한다.
    • 박싱 (Boxing) :
      • 값형을 참조형으로 변환하는 과정을 말함.
      • 값형 변수의 값을 메모리의 힙 영역에 할당된 객체로 래핑
      • 박싱을 하면 값형이 참조형 변수처럼 다뤄질 수 있음
      • 박싱된 값형은 참조로 전달되므로 메모리 오버헤드가 발생할 수 있다.
      • 메모리 오버헤드란?
        • 프로그램의 실행 흐름 도중에 동떨어진 위치의 코드를 실행시켜야 할 때, 추가적으로 시간, 메모리, 사원이 사용되는 현상.
        • 예를 들어, 10초 걸리는 기능이 간접적인 원인으로 20초가 걸린다면 오버헤드는 10초가 되는 것이다.
        • 참고한 블로그 링크 👉 [개발용어] 오버헤드(overhead)란?
    • 언박싱(Unboxing) :
      • 박싱된 객체를 다시 값형으로 변환하는 과정
      • 박싱된 객체에서 값을 추출하여 값형 변수에 할당한다.
      • 언박싱은 명시적으로 타입 캐스팅을 해야 하며, 런타임에서 타입 검사가 이루어진다.
      • 잘못된 형식으로 언박싱하면 런타임 에러가 발생할 수 있다.
    • 싱과 언박싱 예제 :
      • object :
        - 모든 클래스의 직간접적인 상위 클래스
        - 모든 클래스는 object에서 상속되며, object는 모든 형식을 참조할 수 있는 포괄적인 타입이다.

        // 박싱과 언박싱
        int num1 = 10;
        object obj = num1; // 박싱
        int num2 = (int)obj; // 언박싱
        Console.WriteLine("num1: " + num1); // 출력 결과: 10
        Console.WriteLine("num2: " + num2); // 출력 결과: 10
                
                
        // 리스트 활용 예제
        List<object> myList = new List<object>();
        
        // 박싱: 값 형식을 참조 형식으로 변환하여 리스트에 추가
        int intValue = 10;
        myList.Add(intValue); // int를 object로 박싱하여 추가
        
        float floatValue = 3.14f;
        myList.Add(floatValue); // float를 object로 박싱하여 추가
        
        // 언박싱: 참조 형식을 값 형식으로 변환하여 사용
        int value1 = (int)myList[0]; // object를 int로 언박싱
        float value2 = (float)myList[1]; // object를 float로 언박싱
  • 델리게이트, 람다 및 LINQ
    • 리게이트 (Delegate)
      • 메서드를 참조하는 타입 (메서드를 담을 수 있는 변수 같은 느낌)
      • 다른 프로그래밍 언어에서는 함수 포인터라고 부르기도 함
      • 델리게이트를 이용하면 메서드를 매개변수로 전달하거나 변수에 할당할 수 있다.
      • 델리게이트 예제 :
        //델리게이트 선언
        delegate void MyDelegate(string message);
        
        //메서드1
        static void Method1(string message)
        {
            Console.WriteLine("Method1: " + message);
        }
        
        //메서드2
        static void Method2(string message)
        {
            Console.WriteLine("Method2: " + message);
        }
        
        class Program
        {
            static void Main()
            {
                // 델리게이트 인스턴스 생성 및 메서드1 등록
                MyDelegate myDelegate = Method1;
                
                // += 를 통해 여러 메서드를 등록할 수 있다.
                myDelegate += Method2;
        
                // 델리게이트 호출
                myDelegate("Hello!");
                
        				// 사용자가 키를 누를 때까지 대기 (종료 방지) 
                Console.ReadKey();
            }
        }
    • 이벤트 (event) :
      • 델리게이트를 기반으로 만든 멤버. (캡슐화된 델리게이트)
      • 객체간에 이벤트(신호)를 전달하는 역할을 한다.
      • 어떤 일이 발생했을 때 실행할 동작을 등록하고, 필요할 때 실행하는 구조
      • 할당연산자(=) 사용 불가능 (+=, -=는 가능)
      • 델리게이트와 이벤트를 사용해 적의 공격 콜백을 받는 예제 :
        // 델리게이트 선언
        public delegate void EnemyAttackHandler(float damage);
        
        // 적 클래스
        public class Enemy
        {
            // 공격 이벤트
            public event EnemyAttackHandler OnAttack;
        
            // 적의 공격 메서드
            public void Attack(float damage)
            {
                // 이벤트 호출
                OnAttack?.Invoke(damage);
        				// null 조건부 연산자
        				// null 참조가 아닌 경우에만 멤버에 접근하거나 메서드를 호출
            }
        }
        
        // 플레이어 클래스
        public class Player
        {
            // 플레이어가 받은 데미지 처리 메서드
            public void HandleDamage(float damage)
            {
                // 플레이어의 체력 감소 등의 처리 로직
                Console.WriteLine("플레이어가 {0}의 데미지를 입었습니다.", damage);
            }
        }
        
        // 게임 실행
        static void Main()
        {
            // 적 객체 생성
            Enemy enemy = new Enemy();
        
            // 플레이어 객체 생성
            Player player = new Player();
        
            // 플레이어의 데미지 처리 메서드를 적의 공격 이벤트에 추가
            enemy.OnAttack += player.HandleDamage;
        
            // 적의 공격
            enemy.Attack(10.0f); 
        }
    • 람다 (Lambda)
      • 람다를 사용하면 메서드의 이름 없이 메서드를 만들 수 있음.
      • 람다는 델리게이트를 사용하여 변수에 할당하거나, 메서드의 매개변수로 전달할 수 있다.
      • 카드 뒤집기 게임을 만들 때 OrderBy()를 사용하며 람다식을 쓴 적이 있다 👉 2025.01.13 (월) TIL
      • 람다의 기본 형식:
        // 람다의 기본 형식
        (parameter_list) => expression
        
        // 람다를 통해 익명 메서드를 만들어 델리게이트 calc에 저장
        Calculate calc = (x, y) => 
        {	
        		return x + y;
        };
        
        // (x, y)는 메서드의 매개변수, => 뒤의 x, y는 메서드의 반환값이다.
        Calculate calc = (x, y) => x + y;
        
        // 매개변수가 필요없는 메서드라면 괄호는 비워둔다.
        MyDelegate myDelegate = () =>
        {
        		Console.WriteLine("람다식을 통해 전달된 메시지:");
        }
      • 람다 사용 예제:
        // 델리게이트 선언
        delegate void MyDelegate(string message);
        
        class Program
        {
            static void Main()
            {
                // 델리게이트 인스턴스 생성 및 람다식 할당
                MyDelegate myDelegate = (message) =>
                {
                    Console.WriteLine("람다식을 통해 전달된 메시지: " + message);
                };
        
                // 델리게이트 호출
                myDelegate("안녕하세요!");
        
                Console.ReadKey();
            }
        }
        
        [결과]
        람다식을 통해 전달된 메시지: 안녕하세요!
        
    • FuncAction
      • 미리 정의된 제너릭 형식의 델리게이트
      • 제너릭 형식으로 미리 정의되어 있기 때문에 매개변수와 반환 타입을 간결하게 표현할 수 있다.
      • 제너릭이 잘 기억이 안 난다면 👉 2025.01.28 (화)
      • Func :
        • 값을 반환하는 메서드를 나타내는 델리게이트
        • 마지막 제너릭 형식 매개변수는 반환 타입을 나타낸다.
        • 예시: Func<int, string>int를 입력으로 받아 string을 반환하는 메서드
          // Func를 사용하여 두 개의 정수를 더하는 메서드
          int Add(int x, int y)
          {
              return x + y;
          }
          
          // Func를 이용한 메서드 호출
          Func<int, int, int> addFunc = Add;
          int result = addFunc(3, 5);
          Console.WriteLine("결과: " + result);
      • Action :
        • 값을 반환하지 않는 메서드를 나타내는 델리게이트
        • 매개변수를 받아들이지만, 반환타입이 없다.
        • 예시: Action<int, string>intstring을 입력으로 받고, 아무런 값을 반환하지 않는 메서드
          // Action을 사용하여 문자열을 출력하는 메서드
          void PrintMessage(string message)
          {
              Console.WriteLine(message);
          }
          
          // Action을 이용한 메서드 호출
          Action<string> printAction = PrintMessage;
          printAction("Hello, World!");
      • 활용 예제:
        // 게임 캐릭터 클래스
        class GameCharacter
        {
        		// float값을 받고, 반환하는 값이 없는 메서드들을
        		// 연결할 수 있는 델리게이트 healthChangedCallback을 만든다.
        
            private Action<float> healthChangedCallback;
        		
        		// 클래스 내부에서만 접근 가능한 health 변수
            private float health;
        		
        		// health 변수에 접근하기 위한 프로퍼티를 만들어줌
            public float Health
            {
                get { return health; }
                
                // healthChangedCallback는 체력이 변경될 때마다 호출됨.
                set
                {
                    health = value;
                    healthChangedCallback?.Invoke(health);
                }
            }
        		
        		// Action<float> 형식의 callback을 하나 받고
            public void SetHealthChangedCallback(Action<float> callback)
            {
        		    // callback에 담긴 메서드를 healthChangedCallback에 연결해줌
                healthChangedCallback = callback;
            }
        }
        
        // 게임 캐릭터 생성 및 상태 변경 감지
        GameCharacter character = new GameCharacter();    // 캐릭터 생성
        
        // 캐릭터 클래스 안의 SetHealthChangedCallback 메서드를 호출
        // 람다로 Action<float> 형식의 임시 메서드를 만들어 인자로 전달함
        character.SetHealthChangedCallback(health =>
        {
            if (health <= 0)
            {
                Console.WriteLine("캐릭터 사망!");
            }
        });
        
        // 캐릭터의 체력 변경
        character.Health = 0;
    • LINQ (Language Integrated Query)
      • .NET 프레임워크에서 제공되는 쿼리 언어 확장
      • 코드에서 쿼리를 던질 수 있다. 컬렉션이나
      • 데이터 소스(예: 컬렉션, 데이터베이스, XML 문서 등)에서 데이터를 쿼리하고 조작하는데 사용된다.
        • 쿼리란?
          • 직역하면 질문, 문의하다라는 뜻으로 질문에 대한 답을 요청하는 것이다.
          • 쿼리는 데이터베이스DB에 사용자가 요청한 특정 데이터를 보여달라는 요청을 이야기한다.
          • 쿼리문을 작성한다 = DB에서 원하는 정보를 가져오는 코드를 작성한다
          • 참고한 블로그 링크 👉 쿼리 query란 무엇일까?
      • 데이터베이스 쿼리와 유사한 방식으로 데이터를 필터링, 정렬, 그룹화, 조인 등 다양한 작업을 수행할 수 있다.
      • LINQ는 객체, 데이터베이스, XML 문서 등 다양한 데이터 소스를 지원한다.
      • 구조:
        var result = from 변수 in 데이터소스
                     [where 조건식]
                     [orderby 정렬식 [, 정렬식...]]
                     [select];
        • var : 결과 값의 자료형을 자동으로 추론
        • from : 데이터 소스를 지정
        • where : 조건식을 지정하여 데이터를 필터링 (선택적으로 사용)
        • orderby : 정렬 방식을 지정 (선택적으로 사용)
        • select : 조회할 데이터를 지정 (선택적으로 사용)
      • 예제:
        // 데이터 소스 정의 (컬렉션)
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
        
        // 쿼리 작성 (선언적인 구문)
        var evenNumbers = from num in numbers
                          where num % 2 == 0
                          select num;
        
        // 쿼리 실행 및 결과 처리
        foreach (var num in evenNumbers)
        {
            Console.WriteLine(num);
        }
        • numbers 리스트에는 1, 2, 3, 4, 5 정수 값이 들어있음.
        • from num in numbers : 우린 numbers에 있는 것들을 num이라고 부를 건데,
        • where num % 2 == 0 : num이 2의 배수라면
        • select num : 우린 그 2의 배수인 애들을 num으로 가져오겠다.
  • 고급 자료형 및 기능
    • Nullable 형
      • null이란?
        • "아무것도 없음"을 의미
        • 참조형 변수가 어떠한 객체를 참조하지 않을 때 사용된다.
      • Nullable 란?
        • C#에서 null 값을 가질 수 있는 값형에 대한 특별한 형식 (값형은 기본적으로 null을 허용하지 않는다.)
        • 값형 변수에 null 값을 지정할 수 있는 방법을 제공하여 값형이나 구조체를 사용하는 프로그램에서 null 상태를 나타낼 수 있다.
        • 주로 값형 변수가 null인지 아닌지를 확인하고 처리해야 할 때 유용
        • 형식은 ? 연산자를 사용하여 선언
        • 사용 예시:
          // Nullable 형식 변수 선언
          int? nullableInt = null;
          double? nullableDouble = 3.14;
          bool? nullableBool = true;
          
          // 이런 형식도 가능하지만 보통 int? 형식을 선호한다.
          Nullable<int> a = 10;
      • ?? 연산자 (Null Coalescing Operator)
        • null 대신 기본 값을 설정하는 역할
          int? num = null;
          int result = num ?? 100;  // num이 null이면 100 사용
          Console.WriteLine(result);  // 출력: 100
      • ?. 연산자 (Null-Conditional Operator)
        • 객체가 null이면 자동으로 null을 반환해서 오류를 방지함.
          int? number = null;
          Console.WriteLine(number?.ToString());  // 출력 없음 (null 반환)
          
          // 델리게이트 실습 때도 ?. 연산자가 사용되었다.
          public void Attack(float damage)
          {
              // null 참조가 아니라면 이벤트 호출
              OnAttack?.Invoke(damage);
          }
      • ??= 연산자 (Null Coalescing Assignment)
        • 변수가 null일 때만 값을 할당하는 연산자
          int? num = null;
          num ??= 50;  // num이 null이면 50 할당
          Console.WriteLine(num);  // 출력: 50
    • 문자열 빌더 (StringBuilder)
      • StringBuilder 란?
        1. 문자열 조작
          • Append(), Insert(), Replace(), Remove() 등과 같은 다양한 메서드를 통해 문자열에 대한 추가, 삽입, 치환, 삭제 작업을 수행할 수 있음
        2. 변성
          • StringBuilder내부 버퍼를 사용하여 문자열 조작을 수행하므로 크기를 동적으로 조정할 수 있다.
          • 따라서 문자열의 크기가 늘어나거나 줄어들어도 추가적인 메모리 할당이 발생하지 않는다.
          • 다만 StringBuilder의 용량이 무제한은 아니며, 최대 용량이 지정되고 최대 용량을 넘어서게 되면 새 공간이 자동으로 할당되고 용량이 두 배로 증가한다.
        3. 효율적인 메모리 관리
          • 문자열 조작 시 StringBuilder는 내부 버퍼를 사용하여 문자열을 조작하므로, 반복적인 문자열 조작 작업이 발생해도 메모리 할당 및 해제 오버헤드가 크게 감소한다.
      • stringStringBuilder의 차이점 :
        비교 항목string (불변)StringBuilder (가변)
        문자열 수정 가능?❌ 불가능 (새로운 객체 생성)✅ 가능 (기존 객체에서 직접 변경)
        메모리 효율성❌ 낮음 (변경할 때마다 새 객체 생성)✅ 높음 (같은 객체에서 변경)
        속도느림 (반복 수정 시 비효율적)빠름 (반복 수정 최적화)
        사용 예시문자열 변경이 적을 때문자열 변경이 많을 때
      • 주요 메서드
        • Append: 문자열을 뒤에 추가
        • Insert: 문자열을 지정한 위치에 삽입
        • Remove: 지정한 위치에서 문자열을 제거
        • Replace: 문자열의 일부를 다른 문자열로 대체
        • Clear: StringBuilder의 내용을 모두 지움
      • 사용 예제 :
        StringBuilder sb = new StringBuilder();
        
        // 문자열 추가
        sb.Append("Hello");
        sb.Append(" ");
        sb.Append("World");
        
        // 문자열 삽입
        sb.Insert(5, ", ");
        
        // 문자열 치환
        sb.Replace("World", "C#");
        
        // 문자열 삭제
        sb.Remove(5, 2);
        
        // 완성된 문자열 출력
        string result = sb.ToString();
        Console.WriteLine(result);

📍겪은 어려움

  • 개인적으로 인터페이스추상클래스의 차이점을 잘 느낄 수 없어서 그 부분에서 많이 고전했다. (둘의 제일 큰 차이점은 다중 상속 가능/불가능일 텐데 그 차이가 내게 크게 와닿지 않아서인 듯)
  • try-catch를 배울 때에도 굳이 if문을 두고 try-catch를 쓰는 이유를 잘 모르겠어서 이것저것 살피느라 시간을 많이 잡아먹음.
  • 델리게이트까지는 괜찮았으나 람다식이 포함되기 시작하면서 코드 읽기가 점점 힘들어졌던 것 같다.

📍회고 및 반성

  • 배운 건 많은데 이 많은 지식들을 코드에 녹여낼 날이 올까 싶다…
  • 일단 내일부터는 텍스트 RPG 제작에 들어갈 예정! 힘내자👍

0개의 댓글

관련 채용 정보