Unity 내일배움캠프 TIL 0816 (1) | C# 기초 | 인터페이스와 열거형 | 예외 처리 | 값형과 참조형 | 박싱과 언박싱

cheeseonrose·2023년 8월 16일
0

Unity 내일배움캠프

목록 보기
10/89
post-thumbnail

C# 문법종합반 3주차 과제 완성하고 너덜너덜해져서 4주차 강의 듣기 시작. . .
과제 TIL 먼저 써야 되는데 진짜 너ㅁㅜ 귀찮으니까.. 이따 쓸래.............,,

인터페이스와 열거형

다중 상속을 사용하지 않는 이유

  1. 다이아몬드 문제(Diamond Problem)
    • 다중 상속 허용 시 한 클래스가 두 개 이상의 부모 클래스로부터 동일한 멤버를 상속받을 수 있음 -> 이 경우 같은 이름의 멤버를 가지고 있을 때 어떤 부모 클래스의 멤버를 사용해야 하는지 모호해짐
    • 모호함 해결을 위해 코드 복잡성 ↑ 가독성 ↓
  2. 설계 복잡성 증가
    • 클래스 간 관계가 복잡해져서 유지보수성 ↓
  3. 이름 충돌 및 충돌 해결의 어려움
    • 여러 부모 클래스로부터 상속받은 멤버들의 이름이 충돌할 수 있음
    • 코드 복잡성, 오류 발생 가능성 ↑
  4. 설계의 일관성과 단순성 유지
    • C#은 단일 상속을 통해 설계의 일관성과 단순성을 유지하고자 함 -> 코드의 가독성, 이해도 ↑

인터페이스를 사용하는 이유

  1. 코드의 재사용성
    • 다른 클래스에서 인터페이스를 구현하여 동일한 기능 제공 가능
  2. 다중 상속 제공
  3. 유연한 설계
    • 클래스 - 인터페이스 간의 느슨한 결합

인터페이스(Interface)

  • 특징
    • 클래스가 구현해야 하는 멤버들을 정의
    • 클래스에 대한 제약조건을 명시
    • 클래스가 인터페이스를 구현할 때는 모든 인터페이스 멤버를 구현해야 함
    • 다중 상속 지원
  • 구현
    • 인터페이스 이름은 I로 시작할 것
    interface IMyInterface
     {
     	void Method1();
      	int Method2(string str);
     }
     
     class MyClass : IMyInterface
     {
     	public void Method1()
      	{
          	// 구현
      	}
        public int Method2(string str)
        {
        	// 구현
          return 0;
        }
     }
  • 예제
    public interface IUsable
     {
     	void Use();
     }
     
     public class Item : IUsable 
     { 
     	public string Name { get; set; }
      
      	public void Use()
      	{
          	Console.WriteLine("아이템 {0}를 사용했습니다.", Name);
         }
     }
     
     public class Player
     {
     	public void UserItem(IUsable item)
      	{
          	item.Use();
         }
     }
     
     static void Main(string[] args)
     {
     	Player player = new Player();
      	Item item = new Item() { Name = "Health Potion" };  // 매개변수가 아님. 초기화를 위해 값을 미리 세팅한 것
        player.UserItem(item);
     }
  • 다중 상속 예제
    public interface IItemPickable
     {
     	void PickUp();
     }
     
     public interface IDroppable
     {
     	void Drop();
     }
     
     public class Item : IItemPickable, IDroppable
     {
     	public string Name { get; set; };
      
      	public void PickUp()
      	{
      		Console.WriteLine("아이템 {0}을 주웠습니다.", Name);
      	}
          
        public void Drop()
        {
          	Console.WriteLine("아이템 {0}을 버렸습니다.", Name);
        }
     }
     
     public class Player
     {
     	public void InteractWithItem(IItemPickable item)
      	{
      		item.PickUp();
      	}
          
        public void DropItem(IDroppable item)
        {
        	item.Drop();
        }
     }
     
     static void Main()
     {
     	Player player = new Player();
      	Item item = new Item { Name = "Sword" };
      
      	player.InteractWithItem(item);
      	player.DropItem(item);
     }

인터페이스 vs 추상클래스

인터페이스추상클래스
특징추상적인 동작만 정의
구현을 갖지 않음
일부 동작의 구현을 가짐
추상 메서드를 포함할 수 있음
단일 상속만 가능
다른 클래스와 계층 구조 형성 가능
장점클래스 간의 결합도 ↓
유연한 상호작용, 코드 재사용성, 확장성 ↑
코드 중복 방지
확장성 제공
단점인터페이스를 구현하는 클래스는
모든 동작을 구현해야 하므로 작업량 증가
다중 상속 불가능
유연성 제한
  • 상속을 받아 작업 -> 추상클래스
  • 기능들의 호환성 -> 인터페이스

열거형

  • 사용하는 이유
    1. 가독성
      • 연관된 상수들을 명명
      • 상수 사용 시 실수로 잘못된 값을 할당하는 것 방지
    2. 자기 문서화(Self-documenting)
      • 의미 있는 이름 사용 -> 상수 의미가 명확해짐
    3. switch문과 호환성
      • switch문과 사용될 때 유용
  • 특징
    • 서로 관련된 상수들의 집합을 정의할 때 사용
    • 열거형의 각 상수는 정수 값으로 지정됨
  • 구현
    enum MyEnum
     {
     	Value1 = 10,
      	Value2,	// 지정하지 않으면 바로 앞 숫자 + 1
      	Value3 = 20
     }
    MyEnum myEnum = MyEnum.Value1;
    • 형변환
      int intValue = (int)MyEnum.Value1;
       MyEnum enumValue = (MyEnum)intValue;
    • switch문과 사용
      switch (enumValue)
       {
       	case MyEnum.Value1:
          	// Value1 처리
              break;
           case MyEnum.Value2:
           	// Value2 처리
              break;
           case MyEnum.Value3:
           	// Value3 처리
              break;
           default:
           	// 기본 처리
              break;
       }
  • 예제
    public enum Month
     {
     	Jan = 1,
      	Feb,
        Mar,
        Apr,
        May,
        Jun,
        Jul,
        Aug,
        Sep,
        Oct,
        Nov,
        Dec
     }
     
     public static void ProcessMonth(int month)
     {
     	if (month >= (int)Month.Jan && month <= (int)Month.Dec)
      	{
          	Month selectMonth = (Month)month;
            Console.WriteLine("선택한 월은 {0}입니다.", selectMonth);
        } else
        {
        	Console.WriteLine("올바른 월을 입력해주세요.");
         }
     }
     
     static void Main(string[] args)
     {
     	int userInput = 7;
      	ProcessMonth(userInput);
     }



예외 처리

예외란?

  • 프로그램 실행 중 발생하는 예기치 않은 상황

예외 처리의 필요성과 장점

  • 프로그램을 안정적으로 유지하는 데 도움을 줌
  • 프로그램 안정성, 디버깅 용이함 ↑

예외처리 구현

  • try-catch 블록을 사용하여 구현

    • try : 예외가 발생할 수 있는 코드를 작성
    • catch : 예외 처리
    try
     {
     	// 예외가 발생할 수 있는 코드
     }
     catch (ExceptionType1 ex)
     {
     	// ExceptionType1에 해당하는 예외 처리
     }
     catch (ExceptionType2 ex)
     {
     	// ExceptionType2에 해당하는 예외 처리
     }
     finally
     {
     	// 예외 발생 여부와 관계없이 항상 실행되는 코드
     }
  • catch 블록의 우선순위

    • 위에서부터 순서대로 실행
    • 예외 타입에 해당하는 첫 번째 catch 블록이 실행됨
    • 예외 타입이 상속 관계에 있는 경우 상위 예외 타입의 catch 블록 먼저 실행
  • 다중 catch 블록

    • 다양한 예외 타입 처리 가능
  • 예외 객체

    • catch 블록에서는 예외 객체를 사용하여 예외에 대한 정보 액세스 가능

finally

  • 예외 발생 여부와 관계없이 항상 실행되는 코드
  • 예외 발생 시 정리 작업이나 리소스 해제 등의 코드가 포함될 수 있음
  • 생략 가능
  • 실행 시점
    • 예외 발생 : 예외 처리 과정 후 finally 실행
    • 예외 발생 X : 앞 과정 후 실행

사용자 정의 예외 처리

  • Exception 클래스를 상속받아 작성

예제

try
{
	int result = 10 / 0;	// ArithmeticException 발생
    Console.WriteLine("결과 : " + result);
}
catch (DivideByZeroException ex)
{
	Console.WriteLine("0으로 나눌 수 없습니다.");
}
catch (Exception ex)
{
	Console.WriteLine("예외가 발생했습니다 : " + ex.Message);
}
finally
{
	Console.WriteLine("finally 블록이 실행되었습니다.");
}
  • 사용자 정의 예외 처리
    public class NegativeNumberException : Exception
     {
     	// 부모에게 message를 전달해서 먼저 실행 후 내가 정의한 예외 처리를 실행
     	public NegativeNumberException(string message) : base(message) 
      	{
      	}
     }
     
     try
     {
     	int number = -10;
      	if (number < 0)
      	{
          	throw new NegativeNumberException("음수는 처리할 수 없습니다.");
        }
     }
     catch (NegativeNumberException ex)
     {
     	Console.WriteLine(ex.Message);
     }
     catch (Exception ex)
     {
     	Console.WriteLine("예외가 발생했습니다 : " + ex.Message);
     }
    // 플레이어 이동
    try
     {
     	// 플레이어 이동 코드
      if (IsPlayerCollidingWithWall())
      {
      	throw new CollisionException("플레이어가 벽에 충돌했습니다.");
      }
     }
     catch (CollisionException ex)
     {
     	// 충돌 예외 처리
     	Debug.Log(ex.Message);
      	...
     }
    // 리소스 로딩
    try
     {
     	// 리소스 로딩 코드
      	LoadResource("image.png");
     }
     catch (ResourceNotFoundException ex)
     {
     	// 리소스가 없는 경우 예외 처리
     	Debug.Log(ex.Message);
      	...
     }
     catch (ResourceLoadException ex)
     {
     	// 리소스 로딩 중 오류 발생한 경우 예외 처리
      	Debug.Log(ex.Message);
      	...
     }
    // 게임 상태 전이
    try
     {
     	// 상태 전이 코드
      	if (currentGameState != GameState.Playing)
        {
        	throw new InvalidStateException("게임 실행 중이 아닙니다!");
        }
     }
     catch (InvalidStateException ex)
     {
     	// 상태 예외 처리
     	Debug.Log(ex.Message);
      	...
     }



값형과 참조형

값형 (Value Type)참조형 (Reference Type)
변수 저장 방식변수에 값을 직접 저장
-> 변수가 실제 데이터를 보유
변수가 데이터에 대한 참조(메모리 주소)를 저장
변수 할당/전달값이 복사참조가 복사
예시int, float, double, bool 등 기본 데이터 타입클래스, 배열, 인터페이스
  • 값형은 변수가 독립적인 데이터를 가지고, 참조형은 변수가 동일한 데이터를 참조



박싱과 언박싱

  • 박싱(Boxing)
    • 값형을 참조형으로 변환하는 과정
    • 값형 변수의 값을 메모리의 힙 영역에 할당된 객체로 Wrapping
    • 값형이 참조형의 특징을 갖게 하며, 참조형 변수로 다뤄질 수 있음
    • 메모리 오버헤드 발생 가능성
  • 언박싱(Unboxing)
    • 박싱된 객체를 다시 값형으로 변환하는 과정
    • 객체에서 값을 추출하여 값형 변수에 할당
    • 명시적으로 타입 캐스팅을 해야 함
    • 런타임에서 타입 검사가 이루어짐
      -> 잘못된 형식의 언박싱은 런타임 에러를 발생시킴
  • 주요 특징
    • 성능 저하를 초래할 수 있으므로 주의해야 함
    • 박싱된 객체는 힙 영역에 할당되므로 가비지 컬렉션의 대상이 될 수 있음
    • 박싱된 객체와 원래 값형은 독립적 -> 영향 X
  • 예제
    • object : .NET Common Type System(CTS)의 일부이며, 모든 클래스의 직간접적인 상위 클래스
      • 모든 클래스는 object에서 상속됨
      • 모든 형식을 참조할 수 있는 포괄적인 타입
    static void Main()
     {
     	// 값형
      int x = 10;
      int y = x;
      y = 20;
      Console.WriteLine("x: " + x);	// 10
      Console.WriteLine("y: " + y);	// 20
      
      // 참조형
      int[] arr1 = new int[] {1, 2, 3, 4};
      int[] arr2 = arr1;
      arr2[0] = 4;
      Console.WriteLine("arr1[0]: " + arr1[0]);	// 4
      Console.WriteLine("arr2[0]: " + arr2[0]);	// 4
      
      // 박싱과 언박싱
      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 언박싱



너무 길어서 나머지는 다음 TIL로 ~~

1개의 댓글

comment-user-thumbnail
2023년 8월 16일

개발자로서 배울 점이 많은 글이었습니다. 감사합니다.

답글 달기