- object
https://www.youtube.com/watch?v=a7iHDWiSSm4
- 모든 데이터를 다룰 수 있는 특별한 자료형
- object는 참조타입
int a는 값타입이므로 stack에 들어가지만, object a는 heap에 들어간 주소값만을 가지게 된다.
-박싱 (Boxing)
스택 영역 데이터를 힙 영역에 할당하는 것-언박싱 (UnBoxing)
힙 영역 데이터를 스택 영역에 할당하는 것 (값 형식을 언박싱하면 데이터가 복사되고, 기존의 데이터는 가비지가 됨)
-object 클래스 함수
ToString() : 해당 인스턴스가 속한 전체 이름을 반환 (단 C# 기본 자료형(short, int...)은 해당 타입이 가지고 있는 값을 반환(재정의))
GetType() : 클래스를 선언하면 내부적으로 클래스 정보를 가지고 있는 Type 인스턴스가 생성되는데 이를 가지고 올 수 있는 메서드
Equals() : 값을 비교한 결과를 boolean형으로 반환한다. (동일하면 true, 아니면 false)
GetHashCode() : 특정 인스턴스를 고유하게 식별할 수 있는 4바이트 int 값을 반환 (값 형식에서는 데이터가 같으면 똑같은 값이 나옴)
- C# 1.0 컬렉션 (Collection)
https://www.youtube.com/watch?v=xO8bNLfCSMU
같은 데이터 성격을 가진 데이터 모음을 담는 자료구조C# 1.0 컬렉션의 공통 특징
모두 object 기반
→ 박싱/언박싱 필요 (성능 저하 원인)
→ 타입 안정성 부족 (캐스팅 오류 가능성 존재)제네릭 없음
→ 타입별로 클래스를 따로 만들거나, object로 처리해야 했음-ArrayList
using System; using System.Collections; class Program { static void Main() { // ArrayList 생성 ArrayList list = new ArrayList(); // Add: 요소 추가 list.Add(10); // int -> object로 박싱 list.Add("Hello"); list.Add(3.14); // AddRange: 여러 요소 한꺼번에 추가 list.AddRange(new object[] { 100, "World", true }); // Insert: 특정 위치에 요소 삽입 list.Insert(1, "Inserted"); // Remove: 값으로 요소 제거 (첫 번째 일치 항목) list.Remove("Hello"); // RemoveAt: 인덱스로 요소 제거 list.RemoveAt(0); // Contains: 값 존재 여부 확인 bool hasWorld = list.Contains("World"); // true // IndexOf: 값의 인덱스 확인 int index = list.IndexOf("World"); // Count: 현재 요소 개수 int count = list.Count; // Clear: 전체 요소 제거 list.Clear(); // 다시 추가 후 출력 list.Add("One"); list.Add("Two"); list.Add("Three"); // 반복문으로 요소 출력 foreach (object item in list) { Console.WriteLine(item); } } }
메서드 설명 Add(object)
요소 추가 AddRange(ICollection)
여러 요소 한 번에 추가 Insert(index, object)
특정 위치에 요소 삽입 Remove(object)
특정 요소 제거 RemoveAt(index)
인덱스로 요소 제거 Clear()
전체 요소 제거 Contains(object)
해당 값 포함 여부 확인 IndexOf(object)
값의 인덱스 반환 Count
현재 저장된 요소 개수 -Hashtable
using System; using System.Collections; class Program { static void Main() { // Hashtable 생성 Hashtable ht = new Hashtable(); // Add: 키-값 쌍 추가 ht.Add("id", 101); ht.Add("name", "Alice"); ht.Add("isAdmin", true); // 인덱서로 값 접근 (키 사용) Console.WriteLine(ht["name"]); // 출력: Alice // ContainsKey: 키 존재 여부 확인 if (ht.ContainsKey("id")) Console.WriteLine("ID 있음"); // Contains: 값 존재 여부 확인 if (ht.Contains(true)) Console.WriteLine("true 값 있음"); // Remove: 특정 키 삭제 ht.Remove("isAdmin"); // Count: 총 요소 개수 Console.WriteLine("총 개수: " + ht.Count); // 반복문: 모든 키-값 출력 foreach (DictionaryEntry entry in ht) { Console.WriteLine($"{entry.Key} : {entry.Value}"); } // Clear: 모든 요소 제거 ht.Clear(); } }
메서드/속성 설명 Add(key, value)
키-값 쌍 추가 ( 키 중복 시 예외 발생
)Remove(key)
해당 키 삭제 Clear()
전체 요소 제거 ContainsKey(key)
특정 키가 있는지 확인 Contains(value)
특정 값이 있는지 확인 Count
저장된 항목 수 Keys
모든 키 컬렉션 반환 ( ICollection
)Values
모든 값 컬렉션 반환 ( ICollection
)this[key]
인덱서 접근 ( ht["key"] = value;
형태)-Stack
using System; using System.Collections; class Program { static void Main() { // 1. Stack 생성 Stack stack = new Stack(); // 2. Push(object): 요소 추가 stack.Push("Apple"); stack.Push("Banana"); stack.Push("Cherry"); // 현재 스택: Top -> Cherry, Banana, Apple // 3. Peek(): 맨 위 요소 확인 (제거하지 않음) Console.WriteLine("Top: " + stack.Peek()); // Cherry // 4. Pop(): 맨 위 요소 제거 + 반환 Console.WriteLine("Pop: " + stack.Pop()); // Cherry 제거 Console.WriteLine("Pop: " + stack.Pop()); // Banana 제거 // 5. Contains(value): 특정 값 포함 여부 bool hasApple = stack.Contains("Apple"); // true Console.WriteLine("Contains Apple: " + hasApple); // 6. Count: 요소 개수 Console.WriteLine("Count: " + stack.Count); // 1 // 7. Clear(): 모든 요소 제거 stack.Clear(); Console.WriteLine("지운 후 Count: " + stack.Count); // 0 } }
메서드 / 속성 설명 Push(object)
요소 추가 (위에 쌓음) Pop()
맨 위 요소 제거 + 반환 Peek()
맨 위 요소 확인 (제거는 안 함) Contains(object)
값 포함 여부 확인 Clear()
전체 요소 제거 Count
현재 저장된 요소 개수 ToArray()
배열로 변환 (스택 순서 유지됨) -Queue
using System; using System.Collections; class Program { static void Main() { // 1. Queue 생성 Queue queue = new Queue(); // 2. Enqueue(object): 요소 추가 queue.Enqueue("A"); queue.Enqueue("B"); queue.Enqueue("C"); // 현재 큐: Front -> A, B, C // 3. Peek(): 맨 앞 요소 확인 (제거 X) Console.WriteLine("Peek: " + queue.Peek()); // A // 4. Dequeue(): 맨 앞 요소 제거 + 반환 Console.WriteLine("Dequeue: " + queue.Dequeue()); // A 제거 Console.WriteLine("Dequeue: " + queue.Dequeue()); // B 제거 // 5. Contains(value): 값 포함 여부 Console.WriteLine("Contains C? " + queue.Contains("C")); // true // 6. Count: 남은 요소 개수 Console.WriteLine("Count: " + queue.Count); // 1 // 7. Clear(): 모든 요소 제거 queue.Clear(); Console.WriteLine("지운 후 Count: " + queue.Count); // 0 } }
메서드 / 속성 설명 Enqueue(object)
요소를 큐의 뒤에 추가 Dequeue()
앞쪽 요소를 제거하고 반환 Peek()
앞쪽 요소 확인 (제거 X) Contains(object)
특정 값이 있는지 확인 Clear()
모든 요소 제거 Count
현재 요소 수 ToArray()
큐를 배열로 변환 (순서 유지됨)
- C# 2.0 제네릭 (Generic)
https://www.youtube.com/watch?v=gQvTCxcyt_k
- 여러 자료형에 대해 하나의 클래스나 메서드를 재사용할 수 있도록 하는 기능
- 컴파일 시점에 타입을 지정해서, 형변환 없이 타입 안정성 확보 + 성능 향상
- 형식 매개변수
제네릭의 자료형을 나타냄public class Box<T> // T: 형식 매개변수 { public T Value; }
- 제약 조건
형식 매개변수에 넣을 수 있는 타입을 제한하는 기능public class Factory<T> where T : new() { public T Create() => new T(); }
제약 조건 의미 where T : class
참조 타입만 허용 where T : struct
값 타입만 허용 where T : new()
매개변수 없는 생성자 필요 where T : BaseClass
특정 클래스 또는 인터페이스 상속 필요 where T : interface
특정 인터페이스 구현 필요 where T : unmanaged
비관리형 타입만 가능 ( C# 7.3+
)
- 기본값 할당 (default(T))
제네릭에서는 타입을 알 수 없기 때문에, 초기화하려면 default 키워드를 사용public class Box<T> { public T value = default(T); // T가 int면 0, string이면 null }
- 기초문법 마무리
https://www.youtube.com/watch?v=MPmAKNwOHus
- 기본 문법 용어
✅ 1. 예약어 (Keyword)
📌 정의
C# 언어 자체에서 미리 정해진 단어들로, 특별한 의미와 기능을 가진 언어 구성 요소📌 특징
- 변수나 메서드 이름 등으로 사용 불가능
- 컴파일러가 특정 문법을 이해할 때 사용하는 고정된 키워드
📌 예시
int, class, public, if, while, return, using, void, static, new
🔒 예시 코드 (불가능한 경우)
int if = 5; // ❌ 오류: 예약어는 변수 이름으로 사용할 수 없음
✅ 2. 식별자 (Identifier)
📌 정의
개발자가 직접 정의하는 이름
변수, 함수, 클래스, 메서드, 속성, 객체 등의 이름에 사용됨📌 특징
- 첫 글자는 문자나 밑줄(_)로 시작
- 숫자는 두 번째 글자부터 사용 가능
- 예약어는 사용할 수 없음
- 대소문자 구분함 (myVar ≠ MyVar)
📌 예시
int age = 20; // age: 식별자 string name = "Tom"; // name: 식별자
✅ 3. 리터럴 (Literal)
📌 정의
고정된 값 자체를 의미. 코드 안에서 변수에 대입되는 실제 데이터📌 특징
- 값 자체를 의미 (변수 아님)
- 문자열, 숫자, 불리언, 문자 등 여러 타입이 있음
📌 예시
리터럴 종류 예시 정수 리터럴 100
,-5
실수 리터럴 3.14
,-0.01f
문자 리터럴 'A'
,'9'
문자열 리터럴 "Hello"
,""
불리언 리터럴 true
,false
int num = 100; // 100이 리터럴 string s = "hello"; // "hello"가 리터럴
✅ 4. 상수 (Constant)
📌 정의
한 번 정해지면 바뀌지 않는 값
값을 저장하되, 변경 불가. 프로그램 내에서 의미 있는 이름을 부여해 사용📌 특징
- const 키워드 사용
- 컴파일 타임에 값이 결정되어야 함
- 일반적으로 대문자와 밑줄로 표기 (MAX_SIZE, PI)
📌 예시
const double PI = 3.14159; const int MAX_COUNT = 100;
🎯 요약 비교표
구분 의미 예시 예약어 언어에서 정해둔 키워드 int
,class
,return
식별자 프로그래머가 정한 이름 age
,score
,Print()
리터럴 코드에 적힌 고정된 실제 값 100
,"hello"
,true
상수 값이 변하지 않는 변수 const int LIMIT = 10
- 다차원 배열
✅ 1. 1차원 배열
int[] arr = new int[5] { 1, 2, 3, 4, 5 };
✅ 2. 2차원 배열
int[,] arr2 = new int[4, 5] { { 1, 2, 3, 4, 5 }, // 0행 { 6, 7, 8, 9, 10 }, // 1행 { 11,12,13,14,15 }, // 2행 { 16,17,18,19,20 } // 3행 };
[4, 5]: 4행 5열 배열 (총 20칸)
✅ 3. 3차원 배열
int[,,] arr3 = new int[3, 4, 5] { { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, { 11,12,13,14,15 }, { 16,17,18,19,20 } }, { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, { 11,12,13,14,15 }, { 16,17,18,19,20 } }, { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, { 11,12,13,14,15 }, { 16,17,18,19,20 } } };
[3, 4, 5]:
1차원: 깊이(층) → 3개
2차원: 행 → 4개
3차원: 열 → 5개
총 원소 개수 = 3 × 4 × 5 = 60📦 접근 예시
arr3[0, 0, 0] = 1; // 1층 1행 1열 arr3[2, 3, 4] = 20; // 3층 4행 5열
- 삼항 연산자
✅ 삼항 연산자란?
삼항 연산자는 조건에 따라 두 값 중 하나를 선택할 수 있는 간단한 조건문 표현 방식.조건 ? 참일 때의 값 : 거짓일 때의 값;
✅ 예시
int age = 18; string result = (age >= 19) ? "성인" : "미성년자";
age >= 19 조건이 거짓이므로 "미성년자"가 선택됨.
✅ if문과의 비교
// if문 사용 if (score >= 60) result = "합격"; else result = "불합격"; // 삼항 연산자 사용 result = (score >= 60) ? "합격" : "불합격";
결과는 같지만, 삼항 연산자는 코드를 간결하게 만듬.
- goto문
- 지정한 레이블(label) 위치로 흐름을 즉시 이동.
- 일반적인 상황에서는 잘 사용되지 않으며, 가독성과 유지보수를 어렵게 만들 수 있기 때문에 필요할 때만 신중하게 사용하는 것이 권장됨.
goto 레이블이름; ... 레이블이름: 실행할 코드;
✅ 예시
goto는 break, continue, return으로 해결하기 어려운 흐름 제어가 필요할 때, 예외적으로 사용할 수 있음.for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { if (i == 2 && j == 3) goto EndLoop; Console.WriteLine($"i={i}, j={j}"); } } EndLoop: Console.WriteLine("이중 반복문 강제 종료됨");
- goto case / goto default
✅ goto case / goto default란?
- switch 문 안에서 다른 case로 흐름을 이동할 수 있게 해주는 goto.
- 일반 goto와는 다르게, 안전하고 허용된 형태로 자주 사용됨.
✅ 예시: goto case
int number = 2; switch (number) { case 1: Console.WriteLine("1번입니다."); break; case 2: Console.WriteLine("2번입니다. → 3번으로 이동"); goto case 3; // case 3으로 흐름 이동 case 3: Console.WriteLine("3번입니다."); break; default: Console.WriteLine("기본값입니다."); break; }
📌 출력:
2번입니다. → 3번으로 이동
3번입니다.✅ 예시: goto default
string input = "hello"; switch (input) { case "hi": Console.WriteLine("안녕하세요"); break; case "bye": Console.WriteLine("잘 가요"); break; default: Console.WriteLine("기본 인사입니다."); break; case "hello": Console.WriteLine("hello입니다. → default로 이동"); goto default; }
📌 출력:
hello입니다. → default로 이동 기본 인사입니다.
✅ 요약 정리
구문 의미 goto case n;
지정된 case
로 이동goto default;
default
블록으로 이동switch
내에서만 사용 가능일반 코드에서는 사용 불가
- 객체지향
https://www.youtube.com/watch?v=HOfoNakTyQM
- 객체지향 (Object-Oriented Programming, OOP)
현실 세계를 객체라는 단위로 나누고, 이 객체들끼리 상호작용하게 만들어서 프로그램을 구성하는 방식.
특징 설명 예시 캡슐화 데이터와 기능을 하나로 묶고, 외부에서 직접 접근 못하게 보호 private
필드,public
메서드로만 접근상속 기존 클래스의 기능을 물려받고 확장 class Dog : Animal
다형성 같은 함수 호출이 객체에 따라 다르게 동작 virtual
/override
, 인터페이스추상화 불필요한 세부사항은 감추고 중요한 것만 드러냄 인터페이스, 추상 클래스 - 클래스 (Class)
객체를 만들기 위한 설계도.
속성(변수) + 기능(메서드)을 정의한 구조체.📦 클래스 구조 예시
public class Player { // 필드(속성) public string name; private int hp; // 생성자 public Player(string name, int hp) { this.name = name; this.hp = hp; } // 메서드(기능) public void TakeDamage(int damage) { hp -= damage; Console.WriteLine($"{name}이(가) {damage}만큼 피해를 입음 (HP: {hp})"); } }
🎮 객체(Object) 생성 예시
Player p1 = new Player("용사", 100); p1.TakeDamage(10); // 출력: 용사이(가) 10만큼 피해를 입음 (HP: 90)
- 생성자
객체가 생성될 때 자동으로 호출되는 특수한 메서드
주로 초기값 설정에 사용됨📦 기본 형태
public class Player { public string name; public int hp; // 생성자 public Player(string name, int hp) { this.name = name; this.hp = hp; } } // 생성자 호출 예시 (객체 생성) Player p1 = new Player("용사", 100);
- 생성자 오버로딩
public class Enemy { public int hp; // 기본 생성자 public Enemy() { hp = 100; } // 오버로딩된 생성자 public Enemy(int hp) { this.hp = hp; } } Enemy e1 = new Enemy(); // hp = 100 Enemy e2 = new Enemy(250); // hp = 250
- 기본 생성자란
- 매개변수가 없는 생성자
- 클래스에 아무 생성자도 안 만들면 컴파일러가 자동으로 기본 생성자 만들어줌
- 사용자 정의 생성자를 하나라도 만들면, 컴파일러는 기본 생성자를 자동으로 안 만들어줌.
public class Monster { public int hp = 100; } Monster m = new Monster(); // 기본 생성자 자동 호출
- this() 생성자
자기 자신의 다른 생성자를 호출할 때 사용하는 문법
즉, 같은 클래스 안에서 중복 코드 줄이기 위해 씀
- 문법
public 클래스이름(매개변수) : this(다른 생성자의 매개변수) { }
- 예시
public class Enemy { public string name; public int hp; // 생성자 1 public Enemy(string name, int hp) { this.name = name; this.hp = hp; } // 생성자 2 (체력 기본값은 100) public Enemy(string name) : this(name, 100) { } // 생성자 3 (이름도 기본값, 체력도 기본값) public Enemy() : this("이름없음", 100) { } } Enemy e1 = new Enemy(); // 이름없음, 100 Enemy e2 = new Enemy("좀비"); // 좀비, 100 Enemy e3 = new Enemy("보스", 300); // 보스, 300
- 종류자
객체가 소멸될 때 호출되는 메서드
주로 리소스 해제(파일, 핸들, 메모리 등)할 때 사용🔧 기본 문법
~클래스이름() { // 정리할 작업 }
- 이름 앞에 ~ 붙여서 선언
- 매개변수 없음, 오버로딩 불가능
- 직접 호출 불가, GC(Garbage Collector)가 호출함
✅ 예시
public class Monster { public Monster() { Console.WriteLine("몬스터 생성됨"); } ~Monster() { Console.WriteLine("몬스터 제거됨"); // 종료자 } } Monster m = new Monster(); // 생성자 실행 m = null; // 객체 참조 제거 GC.Collect(); // GC 수동 호출 → 종료자 실행될 수 있음
- 종료자 vs Dispose()
항목 종료자 (~Class) Dispose() 호출 주체 GC가 자동 호출 사용자가 명시적으로 호출 제어 가능성 없음 있음 사용 대상 드물게 사용 리소스 관리 시 주로 사용 추천 여부 ❌ 가급적 피함 ✅ 강력 추천 예시: IDisposable 사용 권장
public class FileHandler : IDisposable { private FileStream stream; public FileHandler(string path) { stream = new FileStream(path, FileMode.Open); } public void Dispose() { stream.Close(); Console.WriteLine("파일 닫힘"); } }
- MonoBehaviour
- Unity에서 게임 오브젝트에 부착할 수 있는 스크립트 컴포넌트를 만들기 위한 기본 클래스.
- 게임 오브젝트에 부착한 순간 인스턴스를 생성하여 객체가 되기에 new() 통해 객체를 생성하는 것이 불가능 (그렇기에 초기화는 생성자 대신 아닌 Awake나 Start 사용)