no image
C# - 자료구조
✅ 자료구조란?자료(데이터)를 효율적으로 저장하고, 관리하고, 사용할 수 있도록 구성한 방식쉽게 말하면,"데이터를 어떻게 정리해서 넣고, 꺼내고, 수정할지에 대한 규칙과 구조"✅ 왜 중요한가?빠르게 찾기 위해효율적으로 저장하기 위해복잡한 데이터를 쉽게 다루기 위해성능 좋은 알고리즘을 만들기 위해✅ 비유로 설명하면자료구조 = 서랍장예를 들어, 옷을 아무데나 넣으면 나중에 찾기 힘들지?자료구조는 "양말은 맨 위 칸", "바지는 아래 칸"처럼 정리 기준을 정해주는 거야.✅ 주요 자료구조 종류 (기초)자료구조설명예시배열 (Array)순서대로 데이터를 저장리스트, 숫자 모음리스트 (List)동적으로 크기가 변하는 배열C#의 List스택 (Stack)나중에 넣은 게 먼저 나옴 (LIFO)웹 브라우저 뒤로가기큐 (Q..
2025.07.02
C#
no image
C# - 해쉬셋(HashSet) vs Dictionary(HashTable)
✅ HashSet이란?중복을 허용하지 않는 데이터 집합 (Set)을 구현한 자료구조내부적으로는 **해시 테이블(Hash Table)**을 사용하지만,핵심 목적은 **"중복 없는 값의 모음"**을 만드는 거야.🔍 HashSet의 특징❌ 중복 허용 안 함같은 값을 두 번 넣어도 한 번만 저장됨🔍 순서 없음넣은 순서 보장 안 됨⚡ 매우 빠름추가, 삭제, 탐색 전부 평균 O(1)📦 내부 구조키만 있고, 값은 없음 (딕셔너리는 key-value, 얘는 key만 존재)✅ HashSet 예제 (C#)using System.Collections.Generic;HashSet names = new HashSet();names.Add("Alice");names.Add("Bob");names.Add("Alice"); ..
2025.07.02
C#
no image
C# - 해시테이블(HashTable) vs 딕셔너리(Dictionary)&제네릭(Generic)
✅ 해시테이블 (Hash Table)📌 개념키(key)를 해시 함수(Hash Function)에 넣어 나온 해시값을 기반으로 데이터를 저장하는 자료구조.내부적으로는 배열(array)와 연결 리스트 또는 트리 구조를 함께 사용해 충돌을 해결함.📌 동작 방식Key → 해시 함수 → 인덱스 계산계산된 인덱스에 값을 저장같은 인덱스에 이미 값이 있으면 충돌(Collision) 발생충돌 해결법: 체이닝(Chaining), 오픈 어드레싱(Open Addressing)📌 시간 복잡도 (평균적 상황 기준)삽입: O(1)삭제: O(1)탐색: O(1)※ 최악의 경우에는 O(n) (모든 키가 같은 인덱스로 해시됨)✅ 딕셔너리 (Dictionary in C#)📌 개념C#에서의 Dictionary는 해시테이블 기반으로 ..
2025.07.02
C#
no image
C# - .NET Runtime
✅ .NET 런타임이란?.NET Runtime은 C#, F#, VB.NET 같은 **.NET 언어로 만든 프로그램을 실행해주는 "실행 환경(엔진)"**이야.다시 말해, .NET 코드가 컴퓨터에서 실제로 돌아가도록 도와주는 핵심 소프트웨어야.✅ .NET 런타임이 하는 일 (핵심 역할)역할설명🧠 CLR (Common Language Runtime).NET의 핵심 엔진. 모든 .NET 프로그램은 CLR 위에서 실행됨🧼 GC (Garbage Collector)사용되지 않는 힙 메모리 자동 정리📚 타입 시스템 및 형식 검사코드에서 사용된 타입들이 안전한지 검사🧱 JIT 컴파일 (Just-In-Time)C# 코드를 실행 가능한 기계어로 실행 시에 변환🔒 보안 및 예외 처리프로그램의 오류 관리, 접근 제어 ..
2025.07.01
C#
no image
C# - 캡슐화
✅ 캡슐화(Encapsulation)란?"객체의 내부 상태(데이터)를 외부로부터 숨기고, 허용된 방식으로만 접근하도록 제한하는 것."✅ 핵심 요약항목설명목적데이터를 보호하고, 잘못된 접근/수정을 방지수단접근 제한자 (private, public, protected 등) 사용결과유지보수성 증가, 의도치 않은 버그 감소, 코드 안정성 향상✅ 왜 중요한가?모든 데이터에 마음대로 접근할 수 있다면?예: 체력이 음수로 떨어진다거나, 돈이 마이너스가 되는 상황 발생캡슐화를 통해 이런 비정상적인 상황을 차단할 수 있음✅ 예제 코드 (C# 기준)❌ 잘못된 설계: 데이터가 완전 노출됨public class Player{ public int hp; // 외부에서 직접 접근 가능}Player p = new Playe..
2025.07.01
C#
no image
C# - 박싱/언박싱과 박싱/언박싱&형변환 차이
✅ 박싱(Boxing) & 언박싱(Unboxing)🔸 박싱(Boxing)값 형식(Value Type) 데이터를 **참조 형식(Reference Type)**으로 변환하는 것값 타입(예: int, float)을 object 타입에 저장하면 박싱이 발생이 과정에서 값이 힙에 복사되고, 새로운 참조 객체로 할당됨int num = 10; object obj = num; // 박싱🔸 언박싱(Unboxing)박싱된 object를 다시 원래의 값 타입으로 꺼내오는 것힙에 있는 데이터를 스택에 복사하는 작업반드시 명시적 형변환이 필요하며, 잘못된 형변환 시 런타임 오류 발생int original = (int)obj; // 언박싱🔸 단점박싱/언박싱은 메모리 할당과 복사 비용이 발생 → 성능 저하특히 루프, 게임 로..
2025.07.01
C#
no image
C# Unity - 가비지 컬렉터(GC) 자동실행기준
✅ 가비지 컬렉터(GC)의 역할이란?더 이상 사용되지 않는 힙의 메모리를 자동으로 찾아서 정리해주는 시스템즉,메모리 누수 방지수동으로 메모리 해제하지 않아도 됨 (delete, free 필요 없음)✅ GC의 동작 기준 (언제 실행되나?)GC는 명확한 트리거 조건이 있어. 하지만 언제 실행될지 정확히 예측하긴 어려워.📌 주요 트리거 조건:힙에 여유 공간이 부족할 때새 객체를 new 하려는데 힙에 공간이 부족하면 → GC 실행너무 많은 객체가 생성되었을 때특정 임계치 이상 객체가 쌓이면 → 자동 실행시간이 일정 이상 지났을 때 (백그라운드 GC).NET 런타임이 비활성 시간에 GC를 실행하는 경우GC.Collect()를 명시적으로 호출했을 때가능하긴 하지만, 직접 호출은 성능상 좋지 않아서 권장하지 않음✅..
2025.07.01
C#
Unity 디자인패턴 - 스트래티지 패턴(Strategy Pattern)
스트래티지 패턴 스트래티지 패턴(Strategy Pattern)은 객체 지향 프로그래밍에서 사용되는 디자인 패턴 중 하나로, 알고리즘을 정의하고 이를 캡슐화하여 동적으로 변경할 수 있는 패턴입니다. 이 패턴을 사용하면 알고리즘을 사용하는 클라이언트 코드와 분리하여 각각 독립적으로 변경하고 확장할 수 있습니다. 주로 전략을 변경해야 하는 경우나, 유사한 여러 알고리즘이 존재할 때 유용하게 쓰입니다. 스트래티지 패턴의 구성 요소: 1. 컨텍스트(Context): 전략 객체를 사용하는 역할로, 실제 사용자가 호출하는 메서드가 포함되어 있습니다. 2. 전략(Strategy): 알고리즘을 추상화한 인터페이스나 추상 클래스입니다. 3. 구체적인 전략(Concrete Strategy)L 실제 알고리즘을 구현한 클래스..
2023.08.17

C# - 자료구조

Dev_Jen
|2025. 7. 2. 12:13
반응형

✅ 자료구조란?

자료(데이터)를 효율적으로 저장하고, 관리하고, 사용할 수 있도록 구성한 방식

쉽게 말하면,

"데이터를 어떻게 정리해서 넣고, 꺼내고, 수정할지에 대한 규칙과 구조"


✅ 왜 중요한가?

  • 빠르게 찾기 위해
  • 효율적으로 저장하기 위해
  • 복잡한 데이터를 쉽게 다루기 위해
  • 성능 좋은 알고리즘을 만들기 위해

✅ 비유로 설명하면

  • 자료구조 = 서랍장
    예를 들어, 옷을 아무데나 넣으면 나중에 찾기 힘들지?
    자료구조는 "양말은 맨 위 칸", "바지는 아래 칸"처럼 정리 기준을 정해주는 거야.

✅ 주요 자료구조 종류 (기초)

자료구조설명예시
배열 (Array) 순서대로 데이터를 저장 리스트, 숫자 모음
리스트 (List) 동적으로 크기가 변하는 배열 C#의 List<T>
스택 (Stack) 나중에 넣은 게 먼저 나옴 (LIFO) 웹 브라우저 뒤로가기
큐 (Queue) 먼저 넣은 게 먼저 나옴 (FIFO) 줄서기, 작업 대기열
해시테이블 (Hash Table) 키로 값 빠르게 찾기 전화번호부
트리 (Tree) 계층적 데이터 구조 폴더 구조, 게임 스킬트리
그래프 (Graph) 노드 간 연결 표현 길찾기, SNS 관계망
집합 (Set) 중복 없는 데이터 모음 HashSet
 

✅ 프로그래머 입장에서 왜 중요할까?

  • 자료구조에 따라 코드 성능이 완전히 달라짐
  • 빠르게 검색, 정렬, 삽입하려면 적절한 자료구조 선택이 중요
  • 코딩테스트, 실무 모두 자료구조가 기초체력임

✅ 결론

자료구조는 "데이터를 효율적으로 다루기 위한 도구상자"
잘 쓰면 빠르고, 못 쓰면 느리고 버그 많아져.

 

흠..트리랑 그래프빼고는 다 아는데 트리랑 그래프에 대해서도 공부해봐야겠다!

반응형
반응형

✅ HashSet이란?

중복을 허용하지 않는 데이터 집합 (Set)을 구현한 자료구조
내부적으로는 **해시 테이블(Hash Table)**을 사용하지만,
핵심 목적은 **"중복 없는 값의 모음"**을 만드는 거야.


🔍 HashSet의 특징

❌ 중복 허용 안 함 같은 값을 두 번 넣어도 한 번만 저장됨
🔍 순서 없음 넣은 순서 보장 안 됨
⚡ 매우 빠름 추가, 삭제, 탐색 전부 평균 O(1)
📦 내부 구조 키만 있고, 값은 없음 (딕셔너리는 key-value, 얘는 key만 존재)

✅ HashSet 예제 (C#)

using System.Collections.Generic;

HashSet<string> names = new HashSet<string>();

names.Add("Alice");
names.Add("Bob");
names.Add("Alice");  // 중복! 무시됨

Console.WriteLine(names.Count); // 출력: 2
Console.WriteLine(names.Contains("Bob")); // true

✅ HashSet vs Dictionary (or Hashtable)

키와 값 키만 존재 키와 값 존재
목적 중복 없는 목록 키로 값을 매핑
내부 구현 Hash Table 기반 Hash Table 기반
사용 예 방문 여부 확인, 유니크 목록 설정값 저장, 데이터 검색

✅ 언제 쓰나?

중복 제거 중복 있는 리스트에서 유니크한 값만 뽑을 때
빠른 포함 여부 확인 어떤 값이 존재하는지 빠르게 검사할 때
태그/이름/고유값 저장 "이미 처리했는가?"를 추적할 때

✅ 예: 방문한 적 있는 위치 추적

HashSet<Vector2Int> visited = new HashSet<Vector2Int>();

Vector2Int current = new Vector2Int(2, 3);

if (!visited.Contains(current))
{
    visited.Add(current);
    // 처음 방문한 위치 처리
}

📌 핵심 요약

HashSet<T>는 딕셔너리에서 값(value)을 없앤 버전이라고 생각하면 쉬워.
딕셔너리: key → value
해시셋: key만 저장 (중복 없음)

 

오 해쉬셋과 딕셔너리가 둘다 해시테이블 기반으로 만들어졌다는게 좀 신기했다. 딕셔너리는 해시테이블의 상위호환이지 해시테이블 기반인줄은 몰랐다. 다시 GPT에게 물어보니 해시테이블 기반이 맞다고 한다.

또 궁금한게 생겼다. 딕셔너리에서는 값을 없앤 버전이라고 언급하는데 그럼 딕셔너리는 중복이 허용 가능한가? 싶었다 다시 GPT에게 물어보니

 

딕셔너리(Dictionary)는 키(key) 중복을 허용하지 않아.
✅ 단, 값(value) 은 중복될 수 있어.


✅ 딕셔너리에서 중복이 되는 부분과 안 되는 부분

항목중복 가능 여부설명
Key ❌ 불가능 같은 키로 두 번 Add() 하면 예외 발생
Value ✅ 가능 여러 키가 같은 값을 가질 수 있음

✅ 예시로 확인해보자

 
Dictionary<string, int> dict = new Dictionary<string, int>();

dict.Add("apple", 1);
dict.Add("banana", 1);   // value는 중복 가능
dict.Add("apple", 2);    // ❌ 오류! 키 중복
System.ArgumentException: 항목이 이미 추가되었습니다.

 

즉,

  • "apple"이라는 키가 두 번 들어가는 건 안됨
  • 값인 1이 두 번 들어가는 건 가능함

✅ 해시셋은?

  • 아예 Key = Value인 구조야.
  • 따라서 모든 값 자체가 고유해야 함
  • 즉, "apple"을 두 번 넣으면 무시됨.

🧠 핵심 요약

자료구조중복 가능 여부
Dictionary ❌ 키는 중복 불가 / ✅ 값은 중복 가능
HashSet ❌ 값 중복 불가 (값 = 키)

음..이렇게 된다면 해시테이블도 궁금해졌다.

✅ Hashtable도 Dictionary와 마찬가지로

항목중복 가능 여부
Key ❌ 중복 불가
Value ✅ 중복 가능

 

확실히 딕셔너리도 해시테이블 기반으로 만들어진거다 보니 키값은 둘다 똑같이 중복이 안된다고한다.

정리하자면

HashSet - 키값만 있음, 키값 중복안됨. 약간 데이터 저장용도일까...정보를 넣었는지 안넣었는지 구별할때나 있는지 없는지 구별할떄 많이 사용할 듯 하다.

Dictionary - 키값, 값 둘다있음. 하지만 키값은 중복안됨.

HashTable - 키값, 값 둘다음. 하지만 키값은 중복안됨.

여기서봤을떄는 딕셔너리와 해시테이블이 같아 보이지만. 명확하게 차이가 나는걸 알고있다.

이렇게 계속 비교하면서 공부하는게 참 재밌는거같다

https://dev-jen.tistory.com/entry/%ED%95%B4%EC%8B%9C%ED%85%8C%EC%9D%B4%EB%B8%94HashTable-vs-%EB%94%95%EC%85%94%EB%84%88%EB%A6%ACDictionary%EC%A0%9C%EB%84%A4%EB%A6%ADGeneric

 

C# - 해시테이블(HashTable) vs 딕셔너리(Dictionary)&제네릭(Generic)

✅ 해시테이블 (Hash Table)📌 개념키(key)를 해시 함수(Hash Function)에 넣어 나온 해시값을 기반으로 데이터를 저장하는 자료구조.내부적으로는 배열(array)와 연결 리스트 또는 트리 구조를 함께 사용

dev-jen.tistory.com

 

반응형

'C#' 카테고리의 다른 글

C# - Virtual & abstract  (0) 2025.07.03
C# - 자료구조  (1) 2025.07.02
C# - 해시테이블(HashTable) vs 딕셔너리(Dictionary)&제네릭(Generic)  (0) 2025.07.02
C# - .NET Runtime  (1) 2025.07.01
C# - 캡슐화  (1) 2025.07.01
반응형

✅ 해시테이블 (Hash Table)

📌 개념

  • 키(key)를 해시 함수(Hash Function)에 넣어 나온 해시값을 기반으로 데이터를 저장하는 자료구조.
  • 내부적으로는 배열(array)와 연결 리스트 또는 트리 구조를 함께 사용해 충돌을 해결함.

📌 동작 방식

  1. Key → 해시 함수 → 인덱스 계산
  2. 계산된 인덱스에 값을 저장
  3. 같은 인덱스에 이미 값이 있으면 충돌(Collision) 발생
  4. 충돌 해결법: 체이닝(Chaining), 오픈 어드레싱(Open Addressing)

📌 시간 복잡도 (평균적 상황 기준)

  • 삽입: O(1)
  • 삭제: O(1)
  • 탐색: O(1)

※ 최악의 경우에는 O(n) (모든 키가 같은 인덱스로 해시됨)


✅ 딕셔너리 (Dictionary in C#)

📌 개념

  • C#에서의 Dictionary<TKey, TValue>는 해시테이블 기반으로 구현된 제네릭 키-값(key-value) 쌍 자료구조.
  • 즉, 해시테이블을 제네릭 버전으로 구현한 클래스라고 보면 됨.

📌 사용 예시

Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Alice"] = 25;
ages["Bob"] = 30;

Console.WriteLine(ages["Alice"]);  // 25

📌 주요 메서드

  • Add(key, value) : 새 항목 추가
  • Remove(key) : 항목 제거
  • ContainsKey(key) : 특정 키 존재 여부 확인
  • TryGetValue(key, out value) : 키가 있으면 값을 out 파라미터로 반환

🔍 해시테이블 vs 딕셔너리 차이점

제네릭 지원 ❌ (object 기반)
타입 안정성 낮음 (박싱/언박싱 필요) 높음
성능 상대적으로 느릴 수 있음 더 빠름
쓰레드 안전 기본적으로 안전하지 않음 마찬가지 (별도 처리 필요)
사용 권장 구형 코드 유지 보수시 신규 개발시 권장

 

사실 해시테이블의 상위호환이 딕셔너리 라고 한다. 해시테이블은 사용할떄마다 박싱/언박싱을 하기 때문에 너무 비효율 적이다. 쓰레드까지 들어간다면 솔직히 잘 모르겠다.. 어떻게 안전하지 않다는 기준은 뭔진 모르겠다. 

그리고 제네릭 지원이라는 것이 궁금했다. 제너릭이란건 자세히 뭘까

 

✅ 제네릭이란?

  • 하나의 클래스나 메서드가 다양한 데이터 타입을 처리할 수 있도록 해주는 기능
  • 코드를 타입에 의존하지 않고 작성하고, 나중에 사용할 때 구체적인 타입을 지정
  • 성능이 좋고 타입 안정성도 보장됨 (컴파일 타임에 타입 검사)

🔍 예시 (제네릭 없이 작성한 코드)

ArrayList list = new ArrayList();
list.Add(1);          // int
list.Add("hello");    // string

int num = (int)list[0];  // 형변환 필요 (박싱/언박싱)
  • 타입이 object이기 때문에 어떤 값이든 넣을 수 있지만,
  • 형변환이 필요하고, 런타임 오류가 발생할 수 있음

✅ 제네릭 사용 예시

List<int> intList = new List<int>();
intList.Add(1);
intList.Add(2);

int num = intList[0]; // 형변환 필요 없음!
  • List<int>는 int형만 저장 가능
  • 컴파일 타임에 타입 검사 가능 → 오류 예방
  • 형변환이 없어져서 성능도 좋아짐

🔧 직접 만든 제네릭 클래스 예시

 
public class Box<T>  // T는 타입 매개변수 (Type Parameter)
{
    public T item;

    public void SetItem(T value)
    {
        item = value;
    }

    public T GetItem()
    {
        return item;
    }
}

💡 왜 제네릭을 사용할까?

이유설명
코드 재사용성 하나의 코드로 다양한 타입 처리 가능
타입 안정성 컴파일 타임에 타입 검사 가능
성능 향상 박싱/언박싱 제거로 메모리/속도 효율적

흠..GPT가 BOX<T>라는 예시를 줬는데 이 <T>가 뭔지 정확히 모르겠다 잘 사용하지 않기도 했고. 그래서 물어봤다.

 

✅ Box<T>란?

📦 정의

  • Box<T>는 "어떤 타입 T의 데이터를 담을 수 있는 상자"를 의미하는 클래스야.
  • 여기서 T는 타입 매개변수(Type Parameter) 로, 사용할 때 구체적인 타입으로 대체돼.
public class Box<T>
{
    public T item;

    public void SetItem(T value)
    {
        item = value;
    }

    public T GetItem()
    {
        return item;
    }
}

✅ 핵심 개념

용어설명
T 타입 매개변수. 보통 T(Type), K(Key), V(Value) 같은 문자로 씀
Box<T> T라는 타입을 나중에 지정할 수 있는 클래스
재사용성 Box<int>, Box<string> 등으로 여러 타입에 재사용 가능
 

✅ 실제 사용 예

Box<int> intBox = new Box<int>();
intBox.SetItem(10);                  // int 타입 사용
Console.WriteLine(intBox.GetItem());  // 출력: 10

Box<string> strBox = new Box<string>();
strBox.SetItem("hello");            // string 타입 사용
Console.WriteLine(strBox.GetItem());  // 출력: hello
  • Box<int>라고 선언하면 T는 int로 바뀌고,
  • Box<string>이라고 하면 T는 string으로 바뀌는 거야.

✅ 왜 이렇게 쓰는 걸까?

  1. 타입마다 클래스를 일일이 만들 필요 없음
  2. 형변환 없이 안전하게 값 사용 가능
  3. 컴파일 시 타입 검사 가능 → 오류 예방

✅ Box<T>를 실생활에 비유하면?

  • Box<T>는 "어떤 물건이든 담을 수 있는 상자"
  • 사용할 때 "이 상자에는 **책(int)**만 담을 거야", 혹은 "**편지(string)**만 담을 거야" 하고 말해주는 느낌이야.

흠..일단 그럼 T는 다양한 형식을 받을 수 있지만 그 중에 1종류만 받는다는 느낌이 강하다 생각했다 그러면 <T>라면 정말 어떤 타입이 올지 몰라서 대기하는 느낌이고 평소에 게임을 만든다면 <int>등등 정확한 타입을 써서 정의내리는게 더 깔끔할듯하다. 

✅ 왜 Box<int> 같은 걸 쓸까?

1. 타입별 클래스를 중복 작성할 필요 없게 해줌

❌ 제네릭 없이 하면:

public class IntBox {
    public int item;
}

public class StringBox {
    public string item;
}
  • int, string, float… 다 다른 클래스를 만들어야 해.
  • 유지보수 힘듦 + 코드 중복

✅ 제네릭을 쓰면:

public class Box<T> {
    public T item;
}
  • 한 번만 만들면 어떤 타입이든 다 쓸 수 있어: Box<int>, Box<string>, Box<float>...

2. 타입 안정성과 성능을 동시에 보장

❌ object 기반이면:

 
public class ObjectBox {
    public object item;
}
ObjectBox box = new ObjectBox();
box.item = 10;

int value = (int)box.item; // 형변환 필요 → 실수로 string을 넣었다면? → 런타임 오류
  • object는 어떤 타입이든 담을 수 있지만 형변환 필요하고, 런타임 오류 가능성 있음

✅ 제네릭 쓰면:

Box<int> box = new Box<int>();
box.item = 10;

int value = box.item; // 형변환 없음! 타입 안전!
  • 컴파일 타임에 타입 검사
  • 형변환 없어져서 성능도 더 좋음

3. 자료구조를 만들 때 필수

예를 들어, 우리가 직접 Stack, Queue, List 같은 자료구조를 만든다고 해보자.

public class MyStack<T> {
    private List<T> data = new List<T>();

    public void Push(T item) {
        data.Add(item);
    }

    public T Pop() {
        T item = data[data.Count - 1];
        data.RemoveAt(data.Count - 1);
        return item;
    }
}

📌 결론

Box<int>는 그냥 "정수를 담는 상자"고, 제네릭을 사용함으로써 우리는

  • 중복 코드 없이
  • 타입 안정성 확보하면서
  • 다양한 타입에 대해 유연하게 대응 가능한 클래스를 만들 수 있어.

 

음.. 그렇다고한다 확실히 제네릭을 쓰는게 훨씬 효율적이고 형변환도 필요없으니 좋은듯하다.

반응형

'C#' 카테고리의 다른 글

C# - 자료구조  (1) 2025.07.02
C# - 해쉬셋(HashSet) vs Dictionary(HashTable)  (2) 2025.07.02
C# - .NET Runtime  (1) 2025.07.01
C# - 캡슐화  (1) 2025.07.01
C# - 박싱/언박싱과 박싱/언박싱&형변환 차이  (0) 2025.07.01

C# - .NET Runtime

Dev_Jen
|2025. 7. 1. 12:12
반응형

✅ .NET 런타임이란?

.NET Runtime은 C#, F#, VB.NET 같은 **.NET 언어로 만든 프로그램을 실행해주는 "실행 환경(엔진)"**이야.
다시 말해, .NET 코드가 컴퓨터에서 실제로 돌아가도록 도와주는 핵심 소프트웨어야.


✅ .NET 런타임이 하는 일 (핵심 역할)

역할설명
🧠 CLR (Common Language Runtime) .NET의 핵심 엔진. 모든 .NET 프로그램은 CLR 위에서 실행됨
🧼 GC (Garbage Collector) 사용되지 않는 힙 메모리 자동 정리
📚 타입 시스템 및 형식 검사 코드에서 사용된 타입들이 안전한지 검사
🧱 JIT 컴파일 (Just-In-Time) C# 코드를 실행 가능한 기계어로 실행 시에 변환
🔒 보안 및 예외 처리 프로그램의 오류 관리, 접근 제어 등을 담당
 

✅ .NET 프로그램 실행 과정 요약

  1. C# 코드를 작성
  2. 컴파일 → **IL (중간 언어, Intermediate Language)**로 변환
  3. 실행 시 .NET 런타임(CLR)이
    • IL 코드를 기계어로 변환(JIT)
    • GC로 메모리 관리
    • 예외 처리, 타입 검사 등 실행 환경 제공
  4. 최종적으로 프로그램이 실행됨

✅ .NET Runtime 구성 요소

구성요소설명
CLR (Common Language Runtime) 핵심 실행 엔진
JIT Compiler IL을 기계어로 즉석에서 번역
GC (Garbage Collector) 자동 메모리 관리
CTS (Common Type System) 모든 .NET 언어가 공유하는 타입 규칙
CLS (Common Language Specification) 언어 간 호환성을 위한 최소 규칙
BCL (Base Class Library) System.String, List<T>, Math 등 기본 라이브러리 모음
 

✅ Unity랑 .NET은 어떤 관계?

  • Unity는 C#을 사용하는데, Mono / IL2CPP / CoreCLR 같은 .NET 호환 런타임을 사용해
  • Unity에서의 GC, List<T>, object, Exception 등도 다 .NET 런타임 기반 기능이야

✅ 정리 한 줄 요약

.NET 런타임은 C# 프로그램이 돌아가게 해주는 실행 엔진이며, GC, 예외 처리, 타입 검사, JIT 컴파일 등을 자동으로 담당한다.

반응형

C# - 캡슐화

Dev_Jen
|2025. 7. 1. 11:03
반응형

✅ 캡슐화(Encapsulation)란?

"객체의 내부 상태(데이터)를 외부로부터 숨기고, 허용된 방식으로만 접근하도록 제한하는 것."


✅ 핵심 요약

항목설명
목적 데이터를 보호하고, 잘못된 접근/수정을 방지
수단 접근 제한자 (private, public, protected 등) 사용
결과 유지보수성 증가, 의도치 않은 버그 감소, 코드 안정성 향상

✅ 왜 중요한가?

  • 모든 데이터에 마음대로 접근할 수 있다면?
    • 예: 체력이 음수로 떨어진다거나, 돈이 마이너스가 되는 상황 발생
  • 캡슐화를 통해 이런 비정상적인 상황을 차단할 수 있음

✅ 예제 코드 (C# 기준)

❌ 잘못된 설계: 데이터가 완전 노출됨

public class Player
{
    public int hp;  // 외부에서 직접 접근 가능
}

Player p = new Player();
p.hp = -100; // 💥 논리적으로 말이 안 되는 상태

✅ 캡슐화 적용 예시

public class Player
{
    private int hp = 100; // 외부에서 직접 접근 불가

    public int HP
    {
        get { return hp; }
        set
        {
            if (value >= 0) hp = value;
        }
    }
}
  • 이제 외부에서는 HP 프로퍼티로만 접근 가능
  • if (value >= 0) 같은 검증 로직도 넣을 수 있어

✅ Unity에서 캡슐화 예시

public class Enemy : MonoBehaviour
{
    [SerializeField] private float speed = 3.0f;

    public void SetSpeed(float value)
    {
        if (value >= 0)
            speed = value;
    }

    public float GetSpeed()
    {
        return speed;
    }
}

SerializeField로 에디터에서는 보이게 하되,
스크립트에서는 private하게 유지하는 것도 일종의 캡슐화야


✅ 캡슐화를 위한 주요 키워드 정리

키워드설명
private 외부 접근 불가, 내부에서만 사용 가능
public 외부 접근 허용
protected 자식 클래스는 접근 가능
internal 같은 어셈블리 내에서는 접근 가능
property (get, set) 안전한 접근 인터페이스 제공

✅ 요약 한 줄

캡슐화는 “객체 내부의 상태를 보호하고, 오직 허용된 방식으로만 외부에 접근하게 만드는 것”이다.

 

 

이 내용 또한 회사 면접에서 나왔던 내용이다 개발할떄 잘만 써놓고 용어를 몰라서... 잘 모르곘다고 대답했었다..ㅋㅋㅋㅋㅋ...바보..앞으로는 절대 그럴 일 없겠다..하하...

반응형
반응형

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

🔸 박싱(Boxing)

  • 값 형식(Value Type) 데이터를 **참조 형식(Reference Type)**으로 변환하는 것
  • 값 타입(예: int, float)을 object 타입에 저장하면 박싱이 발생
  • 이 과정에서 값이 힙에 복사되고, 새로운 참조 객체로 할당
int num = 10; object obj = num; // 박싱

🔸 언박싱(Unboxing)

  • 박싱된 object를 다시 원래의 값 타입으로 꺼내오는 것
  • 힙에 있는 데이터를 스택에 복사하는 작업
  • 반드시 명시적 형변환이 필요하며, 잘못된 형변환 시 런타임 오류 발생
int original = (int)obj; // 언박싱

🔸 단점

  • 박싱/언박싱은 메모리 할당과 복사 비용이 발생 → 성능 저하
  • 특히 루프, 게임 로직에서 자주 발생하면 GC 부담으로 이어짐

 

그리고 궁금한게 생겼다 그럼 형변환과 무엇이 차이날까? 싶어서 GPT한테 물어보았다.

 

✅ 형변환(casting) vs 박싱/언박싱의 차이

🔹 형변환(casting)

  • 같은 형식 계열 내에서 타입만 바꾸는 것
  • 메모리 위치나 구조는 그대로
 
float f = 3.14f; int i = (int)f; // 형변환 (float → int)

스택 내에서 값 자체가 바뀌는 것이지, 힙에 복사되는 게 아님

 

✅ 꼭 object로 해야 박싱/언박싱이 일어날까?

🔥 박싱은 object뿐만 아니라 인터페이스로도 발생해!

예시:

interface IPrintable
{
    void Print();
}

struct MyStruct : IPrintable
{
    public void Print() { Console.WriteLine("Hello"); }
}

IPrintable p = new MyStruct(); // ✅ 박싱 발생
  • MyStruct는 값 타입이지만, IPrintable은 참조 타입이기 때문에 박싱이 발생함

 

일단 박싱/언박싱은 확실히 힙까지 사용되지만 형변환은 스택 내에서만 일어나는 일이라는걸 알게됐다!!

 

회사 면접볼때 박싱과 언박싱에 대해서 질문이 나왔던 적이 있다. 사실..여태까지는 잘 생각 안하고 사용했다..기초지식이 확실히 중요한거같다 ㅠㅜ 열심히 공부하자..이제 질문나오면 확실히 대답할 수 있다!!!

 

반응형

'C#' 카테고리의 다른 글

C# - .NET Runtime  (1) 2025.07.01
C# - 캡슐화  (1) 2025.07.01
C# Unity - 가비지 컬렉터(GC) 자동실행기준  (1) 2025.07.01
C# 주사위게임  (0) 2023.03.12
C# 칸 이동 게임  (0) 2023.03.12
반응형

✅ 가비지 컬렉터(GC)의 역할이란?

더 이상 사용되지 않는 힙의 메모리를 자동으로 찾아서 정리해주는 시스템

즉,

  • 메모리 누수 방지
  • 수동으로 메모리 해제하지 않아도 됨 (delete, free 필요 없음)

✅ GC의 동작 기준 (언제 실행되나?)

GC는 명확한 트리거 조건이 있어. 하지만 언제 실행될지 정확히 예측하긴 어려워.

📌 주요 트리거 조건:

  1. 힙에 여유 공간이 부족할 때
    • 새 객체를 new 하려는데 힙에 공간이 부족하면 → GC 실행
  2. 너무 많은 객체가 생성되었을 때
    • 특정 임계치 이상 객체가 쌓이면 → 자동 실행
  3. 시간이 일정 이상 지났을 때 (백그라운드 GC)
    • .NET 런타임이 비활성 시간에 GC를 실행하는 경우
  4. GC.Collect()를 명시적으로 호출했을 때
    • 가능하긴 하지만, 직접 호출은 성능상 좋지 않아서 권장하지 않음

✅ GC는 어떤 기준으로 “삭제할 대상을” 고르나?

GC는 **루트(Root)**에서부터 시작해서, 더 이상 접근할 수 없는 객체를 "가비지"라고 판단해.

🔸 Root란?

  • 스택의 지역 변수
  • 전역 변수 (static)
  • CPU 레지스터에 참조된 객체 등

👉 이 루트에서 참조되고 있지 않은 객체는 **"아무도 쓰고 있지 않다"**고 간주하고 수거함.


✅ GC는 세대를 나눠서 관리한다 (Generational GC)

GC는 성능 최적화를 위해 **객체의 생존 시간을 기준으로 세대(Generation)**를 나눠서 관리해.

세대설명
Gen 0 막 생성된 객체. 대부분 여기서 수거됨 (소멸률 높음)
Gen 1 Gen 0을 넘긴 객체들. 조금 더 오래된 객체
Gen 2 가장 오래 살아남은 객체. 수거 대상에서 가장 오래 남아 있음
 

🔥 대부분의 객체는 Gen 0에서 생기자마자 사라짐
→ 그래서 GC는 Gen 0만 자주 돌리고, Gen 2는 최소한으로만 돌리는 것이 성능에 좋아


✅ 예시 시나리오

void CreateGarbage() { for (int i = 0; i < 100000; i++) { var temp = new object(); // 힙에 생성됨 } }
  • 위 코드를 실행하면 object들이 계속 힙에 생성됨
  • 일정 수 이상이 되면 → GC가 자동으로 실행되어 참조되지 않는 object들을 회수함

 

흠...근데 주요 트리거 조건에서 궁금한게 생겼다.

1. 힙에 여유 공간이 부족한 기준

2. 너무 많은 객체가 생성된다는 기준이 어느정도인지

3. 시간이 일정 이상 지났을때의 기준

GPT에게 물어보니 친절하게 설명해줬다.

✅ 1. 힙에 여유 공간이 부족할 때 → GC 실행

🔸 그럼 “여유 공간 부족”의 기준은 뭘까?

정해진 “절대 크기”는 없음.
대신 세대(Generation)별 할당량과 동적 상황에 따라 달라짐.

📌 핵심 개념: Allocation Budget (할당 예산)

  • GC는 Gen 0/1/2 각각에 대해 힙 할당 예산을 갖고 있음
  • 이 예산을 초과하면 → GC가 실행됨
  • 예산은 GC가 이전에 수거한 양, 메모리 압력, CPU 상황 등을 고려해 동적으로 조절됨

📌 예시:

  • Gen 0 힙이 100KB 할당 예산을 갖고 있고, 지금까지 95KB 사용됨
  • 그런데 new로 10KB 객체를 만들려고 하면 → 예산 초과
    → ✅ GC가 Gen 0에서 먼저 동작

💡 즉, **"GC는 얼마나 썼냐"보다는 “할당하려는 크기 + 예산 초과 여부”**로 판단한다는 점이 중요!


✅ 2. 너무 많은 객체가 생성되었을 때 → GC 실행

🔸 기준은?

역시 정해진 개수 기준은 없음.
GC는 “많이 생성되었다”는 양적 기준보다, “할당 속도와 생존률”을 중요하게 본다.

📌 GC는 객체 생존률을 보고 판단

  • GC는 "이번에 생성된 객체들이 얼마나 빨리 죽는가?"를 본다
  • 생성만 많이 되고 죽지 않으면, 세대(Gen) 올라가며 압력 증가
  • 그럼 GC는 더 자주 실행됨

📌 예시 상황:

  • for 루프에서 수만 개의 new 연산이 빠르게 실행됨
  • 대부분은 Gen 0에서 할당되고, 금방 참조가 끊어짐
    → GC는 빠르게 Gen 0만 자주 실행

✅ 3. 시간이 일정 이상 지났을 때 (백그라운드 GC)

🔸 기준은?

⏱️ 이건 명확히 시간 초 단위로 정해져 있는 게 아님
대신 GC가 백그라운드 스레드에서 “유휴 시간”을 감지할 때 실행됨

📌 백그라운드 GC가 동작하는 상황:

  • 앱이 입력을 기다리거나, 유저가 아무것도 하지 않을 때
  • GC는 작업이 적은 타이밍에 자동 실행 → 앱이 바쁠 땐 일부러 안 함

✅ 보충 정리: GC의 실행 타이밍은 “예측 불가능하지만, 패턴은 있다”

트리거 상황설명
힙 공간 부족 할당 예산 초과 시
객체 생성량 급증 단기간에 많은 new → GC 주기 짧아짐
유휴 시간 감지 백그라운드에서 앱 부하 없을 때
명시적 호출 GC.Collect() → 강제 실행 (권장 ❌)
 

✅ 그럼 우리가 할 수 있는 건?

  • GC 타이밍을 완전히 제어할 수는 없지만,
  • 다음과 같은 습관을 통해 예측 가능하고 최소화된 GC 동작을 만들 수 있어:

🔹 GC 최적화 팁 (특히 Unity 기준)

  • Update()에서 new 피하기
  • List.Clear()로 컬렉션 재사용
  • Object Pooling 활용
  • string 연결 시 StringBuilder 사용
  • 큰 객체는 필요할 때만 생성 & 사용 후 null 처리

✅ 요약

질문정리
1. 힙 공간 부족 기준은? 할당 예산 초과가 기준 (세대별 동적 조절)
2. “많은 객체 생성” 기준은? 객체 생성 속도 + 생존률을 종합적으로 판단
3. “시간이 지나면 실행” 기준은? 앱이 유휴 상태일 때 백그라운드에서 감지하여 실행

 

그렇다고 한다! 1번은 정해진 절대적인 크기는 없다고하니 음.. 대신 세대별 할당량과 동적 상황에 달라지고 2번은 객체의 생존률을 보고 판단하고 3번은 유휴 시간을 감지하면 한다니

컴퓨터마다 다 성능도 다르고 코딩할때도 다 설정값이 다르니 계산된 값에 따라 컴퓨터에 따라 달라지지 않을까 싶다. 웬만하면 비슷할 지도?

 

예전에 회사에서 면접을 볼떄 가비지컬렉터가 작동할떄 언제 자동으로 작동되냐? 라고 질문을 받은적이 있었다. 근데 나도 생각해보니 자동으로 처리해주는것만 알았지 어떻게 자동으로 처리해주는지에 대한 내용은 잘 몰랐다. 스파르타 임예찬 튜터님께 물어보니 잘 설명해주셨고 나머지 더 자세한 부분은 GPT한테 물어보니 잘 대답해줬다. 궁금내용 해결!

반응형

'C#' 카테고리의 다른 글

C# - 캡슐화  (1) 2025.07.01
C# - 박싱/언박싱과 박싱/언박싱&형변환 차이  (0) 2025.07.01
C# 주사위게임  (0) 2023.03.12
C# 칸 이동 게임  (0) 2023.03.12
C# 구구단  (0) 2023.03.12
반응형

스트래티지 패턴

스트래티지 패턴(Strategy Pattern)은 객체 지향 프로그래밍에서 사용되는 디자인 패턴 중 하나로,
알고리즘을 정의하고 이를 캡슐화하여 동적으로 변경할 수 있는 패턴입니다.
이 패턴을 사용하면 알고리즘을 사용하는 클라이언트 코드와 분리하여 각각 독립적으로 변경하고 확장할 수 있습니다.
주로 전략을 변경해야 하는 경우나, 유사한 여러 알고리즘이 존재할 때 유용하게 쓰입니다.

스트래티지 패턴의 구성 요소:
1. 컨텍스트(Context): 전략 객체를 사용하는 역할로, 실제 사용자가 호출하는 메서드가 포함되어 있습니다.
2. 전략(Strategy): 알고리즘을 추상화한 인터페이스나 추상 클래스입니다.
3. 구체적인 전략(Concrete Strategy)L 실제 알고리즘을 구현한 클래스들 입니다.

스트래티지 패턴 예시 코드:

 

// 전략(Strategy) 인터페이스
public interface IAttackStrategy
{
    void Attack();
}

// 구체적인 전략(Concrete Strategy) 클래스들
public class MeleeAttackStrategy : IAttackStrategy
{
    public void Attack()
    {
        Console.WriteLine("근접 공격을 수행합니다.");
    }
}

public class RangedAttackStrategy : IAttackStrategy
{
    public void Attack()
    {
        Console.WriteLine("원거리 공격을 수행합니다.");
    }
}

// 컨텍스트(Context) 클래스
public class Character
{
    private IAttackStrategy attackStrategy;

    public void SetAttackStrategy(IAttackStrategy strategy)
    {
        attackStrategy = strategy;
    }

    public void PerformAttack()
    {
        if (attackStrategy != null)
        {
            attackStrategy.Attack();
        }
    }
}

// 클라이언트 코드
class Program
{
    static void Main(string[] args)
    {
        Character warrior = new Character();
        Character archer = new Character();

        warrior.SetAttackStrategy(new MeleeAttackStrategy());
        archer.SetAttackStrategy(new RangedAttackStrategy());

        warrior.PerformAttack(); // Output: 근접 공격을 수행합니다.
        archer.PerformAttack();  // Output: 원거리 공격을 수행합니다.
    }
}

위의 예시 코드에서 IAttackStrategy 인터페이스는 알고리즘을 추상화합니다. 
MeleeAttackStrategy와 RangedAttackStrategy는 이 인터페이스를 구현하여 각각의 공격 방식을 
구체적으로 정의합니다. Character 클래스는 전략을 변경할 수 있는 메서드를 제공하고, 
클라이언트 코드에서 해당 전략을 설정하고 공격을 수행합니다.

스트래티지 패턴을 사용하면 새로운 공격 방식을 추가하거나 기존 공격 방식을 변경해야 할 때, 
기존 코드의 수정 없이 새로운 전략을 추가하거나 교체할 수 있습니다.

반응형