팀스파르타 코딩

📚 TIL - 2025년 4월 16일 (수) / C# 문법 학습(인터페이스, 열거형, 예외처리, 델리게이트)

creator2041 2025. 4. 16. 16:49

 

📌 01. 인터페이스와 열거형

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

  • 다이아몬드 문제(Diamond Problem): 동일한 멤버를 두 부모 클래스로부터 상속받을 경우 충돌 발생 가능.
  • 설계 복잡성 증가: 클래스 관계가 복잡해지고 유지보수가 어려워짐.
  • 이름 충돌 가능성: 동일한 이름의 멤버 충돌 처리 필요.
  • C#의 단일 상속 원칙: 깔끔한 구조 유지를 위해 인터페이스로 대체.

🔹 인터페이스 사용 이유

  • 재사용성: 다양한 클래스가 같은 동작 구현 가능.
  • 다중 상속 가능: 클래스는 단일 상속이지만 인터페이스는 다중 구현 가능.
  • 유연한 설계: 느슨한 결합 구조 가능.

🔹 인터페이스란?

  • 인터페이스는 메서드, 속성 등의 **형식(시그니처)**만을 정의하는 일종의 설계도다.
  • **구현(내용)**은 포함하지 않으며, 이 인터페이스를 사용하는 클래스가 해당 멤버들을 직접 구현해야 한다.
  • 마치 "이런 기능이 있어야 해!"라고 규칙을 강제하는 느낌.
// 인터페이스 정의
interface IMyInterface
{
    void Method1();
    int Method2(string str);
}

// 인터페이스를 구현한 클래스
class MyClass : IMyInterface
{
    public void Method1() { }
    public int Method2(string str) => 0;
}

인터페이스를 통해 클래스가 특정 기능을 반드시 구현하도록 강제할 수 있다.

🔹 게임 응용 예시

  • 이동 인터페이스
public interface IMovable { void Move(int x, int y); }

public class Player : IMovable
{
    public void Move(int x, int y)
    {
        Console.WriteLine($"플레이어가 ({x}, {y})로 이동합니다");
    }
}

다양한 게임 객체가 동일한 방식으로 '이동'이라는 기능을 구현하도록 할 수 있다.

  • 아이템 인터페이스 예시
public interface IUsable { void Use(); }

public class Item : IUsable
{
    public string Name { get; set; }
    public void Use() => Console.WriteLine($"{Name}을 사용했습니다");
}

소비 가능한 아이템이라면 IUsable을 통해 일관된 사용 로직을 제공할 수 있다.

  • 다중 인터페이스 구현 예시
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($"{Name}을 주웠습니다");
    public void Drop() => Console.WriteLine($"{Name}을 버렸습니다");
}

하나의 클래스에서 다양한 인터페이스를 통해 여러 기능을 동시에 지원 가능.

🔹 인터페이스 vs 추상 클래스

구분 인터페이스 추상 클래스

상속 다중 구현 가능 단일 상속만 가능
멤버 구현 구현 없음 구현 포함 가능
사용 동작 정의 공통 기능 공유

인터페이스는 규약(약속), 추상 클래스는 기반 기능 제공의 느낌.


📌 02. 열거형 (Enum)

🔹 개념 설명

  • 열거형은 관련된 상수들의 집합이다.
  • 정수에 이름을 부여한 것으로, 코드 가독성과 유지보수에 큰 도움을 준다.
  • 내부적으로는 정수형이며, 기본값은 0부터 시작한다.

🔹 예시

// 요일 열거형 정의
enum DaysOfWeek { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }

DaysOfWeek today = DaysOfWeek.Monday;
Console.WriteLine("오늘은 " + today);  // 출력: 오늘은 Monday
// 게임 아이템 희귀도 열거형
enum ItemRarity { Common, Uncommon, Rare, Epic }

숫자 대신 의미 있는 이름을 부여하여 가독성과 유지보수를 향상시킨다.


📌 03. 예외 처리 및 값/참조형

🔹 예외 처리 개념

  • **예외(Exception)**란 실행 도중 발생하는 오류 상황이다.
  • 예외 처리는 프로그램의 비정상 종료를 방지하고, 오류를 우아하게 처리하게 해준다.
try
{
    int result = 10 / 0;
}
catch (DivideByZeroException ex)
{
    Console.WriteLine("0으로 나눌 수 없습니다.");
}
finally
{
    Console.WriteLine("항상 실행됨");
}

try: 예외 가능성 있는 코드, catch: 오류 발생 시 처리, finally: 무조건 실행되는 블록.

🔹 사용자 정의 예외

public class MyException : Exception
{
    public MyException(string msg) : base(msg) { }
}

예외 클래스를 직접 정의하면 상황별로 더 구체적인 오류 대응이 가능하다.

🔹 값형 vs 참조형

  • 값형(Value Type): 값을 직접 저장. 복사되므로 독립적.
  • 참조형(Reference Type): 주소(참조)를 저장. 같은 데이터 공유함.
int a = 5;
int b = a;
b = 10; // a는 여전히 5

MyClass obj1 = new MyClass();
MyClass obj2 = obj1;
obj2.Value = 100; // obj1.Value도 100으로 바뀜

🔹 박싱(Boxing) & 언박싱(Unboxing)

  • 박싱: 값형 → 참조형으로 변환 (힙에 저장)
  • 언박싱: 참조형(object) → 값형으로 꺼내는 작업
int x = 10;
object obj = x;       // 박싱
int y = (int)obj;     // 언박싱

많이 쓰이진 않지만, 컬렉션(List) 등에서 타입이 섞일 때 활용된다.


📌 04. 델리게이트, 람다, LINQ

🔹 델리게이트 개념

  • 메서드를 변수처럼 다루게 해주는 타입이다.
  • 함수 포인터 느낌. 콜백 구조 만들기 좋음.
delegate int Calculator(int x, int y);

Calculator calc = (a, b) => a + b;
Console.WriteLine(calc(3, 5));  // 출력: 8

🔹 람다식 (Lambda)

  • 메서드 이름 없이 쓰는 간단한 익명 함수.
Action<string> print = msg => Console.WriteLine(msg);
print("안녕하세요!");

이벤트 등록, LINQ 처리 등에 주로 쓰인다.

🔹 Func & Action

Func<int, int, int> adder = (a, b) => a + b;
Action<string> printer = s => Console.WriteLine(s);

Func은 반환형 있음 / Action은 반환형 없음

🔹 LINQ 기본 예제

  • LINQ는 컬렉션 데이터를 SQL처럼 쿼리할 수 있게 해주는 문법이다.
List<int> nums = new List<int>{ 1, 2, 3, 4, 5 };
var evenNums = from n in nums where n % 2 == 0 select n;

foreach (var n in evenNums)
    Console.WriteLine(n);  // 2, 4

📌 05. 고급 자료형 - Nullable, StringBuilder

🔹 Nullable 형식

  • 원래 값형(int, float 등)은 null을 가질 수 없다.
  • int?처럼 ?를 붙이면 null 허용이 된다.
int? score = null;
int finalScore = score ?? 0;  // null이면 0으로 대체

?? 연산자는 null 대체 값 제공 (null-coalescing operator)

🔹 StringBuilder

  • 문자열을 반복해서 연결하거나 조작할 때는 +보다 효율적.
  • 내부적으로 가변 버퍼를 이용해서 문자열을 구성한다.
StringBuilder sb = new StringBuilder();
sb.Append("Hello").Append(" World");
string msg = sb.ToString();
Console.WriteLine(msg);

문자열 조작이 많은 경우 꼭 써야 하는 도구.


🎯 유니티가, 아니 다 떠나서 게임엔진이라는게 괜히 GOAT 가 아니구나... 거긴 컴포넌트만 끼우면 알아서 다 해줬는데, 지금은 진짜 내가 머리로 구조를 다 짜야 하니까.

 

그래도 이제는 코드가 조금씩 읽히고, "아 이건 이 기능이구나" 싶은 것도 느껴지긴 해. 근데 막상 백지에서 똑같이 직접 쓰라고 하면 진짜 아무 생각도 안 나서 멍해진다. 그래서 더더욱 이번 정리가 중요하다고 느낌. 적어도 이 문서 계속 반복해서 보면서 복습하면 개념이 선명해질 거라는 희망은 있음.

 

다음 주 시험 준비하면서 이 문서를 몇 번씩 읽고, 손으로 예제도 타이핑하면서 익숙해지려고 한다. 지금은 감으로 뭔가를 따라치는 느낌이 강한데, 결국은 내가 설계부터 직접 할 수 있어야 하잖아. 아직은 어렵지만, 계속 반복하면 언젠가는 진짜 내 머리에서 기능이 그려지고 손이 먼저 나가겠지.

 

그래도 지금은 적어도 코드가 "읽히는" 상태인 게 다행이라고 생각한다. 처음엔 뭔 말인지도 몰랐는데, 이제는 "아 이런 뜻이구나"는 알겠거든. 다음엔 내가 실제로 "게임에 어떻게 쓰일지"까지 자연스럽게 떠올릴 수 있었으면 좋겠다. 아직 갈 길은 멀지만, 유니티 없이도 조금씩 적응해 가는 중이라는 데 의의를 둔다.