📌 LINQ를 활용한 문자열 배열 정렬 및 검색
string[] names = { "Charile", "Alice", "Bob" };
// 문자열 배열을 정렬
var sortedNames = names.OrderBy(n => n);
//int Identity(int n)
//{
// return n;
//}
foreach(var name in sortedNames)
{
Console.WriteLine(name);
}
// 'A'로 시작하는 첫 번째 이름 찾기
var firstName = names.First(n => n.StartsWith("A"));
Console.WriteLine($"First name starting with A: {firstName}");
- OrderBy(n => n)를 사용하여 알파벳순(A~Z)으로 정렬합니다.
- OrderBy는 LINQ 메서드로, 내부적으로 정렬된 컬렉션을 반환합니다.
- sortedNames는 정렬된 IEnumerable<string> 형태의 컬렉션을 가지게 됩니다.
- First(n => n.StartsWith("A"))
- A로 시작하는 첫 번째 이름을 찾음
- First()는 조건을 만족하는 첫 번째 요소를 반환하는 LINQ 메서드
- 만약 A로 시작하는 이름이 없으면 예외(Exception)가 발생합니다.
📌 LINQ 메서드 구문 vs. 쿼리 구문 비교
int[] nums = { 5, 3, 8, 1 };
// 메서드 구문
var sortedMethod = nums.OrderBy(n => n);
// 쿼리 구문
var sortedQuery = from n in nums
orderby n
select n;
Console.WriteLine("Method syntax: ");
foreach (var n in sortedMethod) Console.WriteLine(n);
Console.WriteLine("Query syntax: ");
foreach (var n in sortedQuery) Console.WriteLine(n);
1️⃣ 메서드 구문(Method Syntax)
- OrderBy(n => n): nums 배열을 오름차순으로 정렬
- 람다 표현식(=>)을 활용하여 OrderBy를 적용
- **LINQ의 확장 메서드(Extension Method)**를 사용
✔ 장점
- 간결한 코드
- 체이닝(Chaining) 가능 → 여러 개의 LINQ 메서드를 연결하여 사용 가능
2️⃣ 쿼리 구문(Query Syntax)
- SQL 스타일의 LINQ 쿼리 표현식
- from n in nums → nums 배열에서 데이터를 가져옴
- orderby n → 정렬 수행 (기본적으로 오름차순)
- select n → 정렬된 값을 선택하여 반환
✔ 장점
- SQL과 유사하여 직관적인 가독성 제공
- 복잡한 데이터 조작 시 가독성이 향상됨
📌 LINQ Select()를 활용한 문자열 길이 추출
string[] words = { "apple", "banana", "cherry" };
// Select()를 사용하여 각 단어의 길이를 추출
var lengths = words.Select(w => w.Length);
foreach(var length in lengths)
{
Console.WriteLine(length);
}
- Select()는 LINQ 메서드로, 컬렉션의 각 요소를 변환하는 데 사용됨
- w => w.Length:
- 각 문자열 w의 길이를 가져와 반환
- 변환 결과: IEnumerable<int> 형태의 컬렉션을 반환
Select()를 사용하면?
기능 | 설명 |
데이터 변환 | 원본 컬렉션을 가공하여 새로운 컬렉션 생성 |
문자열 길이 추출 | 각 단어의 .Length 속성을 가져와 숫자로 변환 |
가독성 | 간결한 코드로 데이터 변환 가능 |
📌 여러 알고리즘
1. 합계 구하기 SUM
int[] data = { 1, 2, 3, 4, 5 };
int sum = 0;
foreach (var d in data)
{
sum += d;
}
Console.WriteLine($"Sum: {sum}"); //15
2. 개수 구하기 Count
//count
int[] data = { 1, 2, 3, 4, 5 };
int count = data.Length;
Console.WriteLine($"Count: {count}");//5
3. 평균 구하기 Average()
int[] data = { 1, 2, 3, 4, 5 };
double avg = data.Average();
Console.WriteLine($"Average: {avg}"); //3
4. 최댓값 구하기 Max()
int[] data = { 10, 3, 5, 2, 8 };
int max = data.Max();
Console.WriteLine($"Max : {max}");//10
5. 최솟값 구하기 Min()
int[] data = { 55, 66, 48 };
int min = data.Min();
Console.WriteLine($"Min : {min}");//48
6. 근삿값 구하기
배열에서 특정 값에 가장 가까운 값을 찾는 예제
int[] data = { 10, 20, 30, 43, 55 };
int target = 22;
int nearest = data[0];
foreach (var d in data)
{
if (Math.Abs(d - target) < Math.Abs(nearest - target))
nearest = d;
}
Console.WriteLine($"Nearest to {target}: {nearest}");//20
Math.Abs() 메서드는 주어진 숫자의 **절댓값(Absolute Value)**을 반환합니다.
절댓값이란 숫자의 부호(+, -)를 제거한 값으로, 음수는 양수로 변환되고 양수는 그대로 유지됩니다
7. 순위 구하기
순위를 계산하는 방법:
- 점수가 높은 학생이 낮은 순위를 가져야 함 (예: 1등, 2등, 3등...)
- 특정 점수보다 더 높은 점수를 가진 개수만큼 순위를 증가시킴.
int[] scores = { 90, 70, 50, 70, 60 };
for (int i = 0; i < scores.Length; i++)
{
int rank = 1;
for (int j = 0; j < scores.Length; j++)
{
if (scores[j] > scores[i]) rank++;
}
Console.WriteLine($"Score: {scores[i]}, Rank:{rank}");
}
i | 비교(j) | scores[j] > scores[i] 조건 | rank 증가 | 최종 rank |
90 | 70, 50, 70, 60 | false 모두 | rank = 1 유지 | 1 |
70 | 90, 50, 70, 60 | 90 > 70 → +1 | rank = 2 | 2 |
50 | 90, 70, 70, 60 | 90 > 50, 70 > 50, 70 > 50, 60 > 50 → +4 | rank = 5 | 5 |
70 | 90, 50, 70, 60 | 90 > 70 → +1 | rank = 2 | 2 |
60 | 90, 70, 50, 70 | 90 > 60, 70 > 60, 70 > 60 → +3 | rank = 4 | 4 |
8. 순서대로 나열하기 : Sort()
int[] data = { 5, 2, 8, 1, 9 };
Array.Sort(data);
foreach (var d in data) Console.WriteLine(d);
9. 특정 값 검색하기
int[] data = { 5, 2, 8, 1, 9 };
int target = 8;
int index = -1;
for(int i=0; i<data.Length; i++)
{
if (data[i] == target)
{
index = i;
break;
}
}
Console.WriteLine(index >= 0 ? $"Found at index {index}" : "Not found");
//Found at index 2
10. 그룹화하기 : GroupBy()
//그룹
string[] fruits = { "apple", "banana", "blueberry", "cherry", "apricot" };
var groups = fruits.GroupBy(f => f[0]);//첫 글자로 그룹화
foreach(var group in groups)
{
Console.WriteLine($"Key:{group.Key}");
foreach(var item in group){
Console.WriteLine($" {item}");
}
}
📌상속(Inheritance) 개념
// 부모 클래스 (Animal)
class Animal
{
public string Name { get; set; } // 동물의 이름 속성
public void Eat()
{
Console.WriteLine($"{Name}이(가) 음식을 먹고 있습니다.");
}
}
// 자식 클래스 (Dog) - Animal 클래스를 상속
class Dog : Animal
{
public void Bark()
{
Console.WriteLine($"{Name}이(가) 멍멍 짖습니다!");
}
}
class Program
{
static void Main(string[] args)
{
Dog myDog = new Dog(); // Dog 객체 생성
myDog.Name = "바둑이"; // 부모 클래스(Animal)의 속성 사용
myDog.Eat(); // 부모 클래스의 메서드 호출 가능
myDog.Bark(); // 자식 클래스의 메서드 호출
}
}
✅ 개념 정리
🔹 상속(Inheritance)
- 부모 클래스(Animal)의 속성과 메서드를 자식 클래스(Dog)가 물려받는 것
- Dog 클래스는 Animal 클래스를 상속하므로 Name 속성과 Eat() 메서드를 그대로 사용할 수 있음
🔹 상속의 장점
✔ 코드 재사용 → 부모 클래스의 기능을 자식 클래스에서 그대로 활용
✔ 유지보수 용이 → 부모 클래스의 기능을 수정하면 자식 클래스도 반영됨
✔ 확장성 증가 → 자식 클래스에서 기능을 추가하여 새로운 동작을 정의 가능
📌메서드 오버라이딩(Overriding) 개념 정리
✅ 오버라이딩(Overriding)이란?
🔹 부모 클래스의 메서드를 자식 클래스에서 재정의하는 기능
🔹 부모 클래스의 메서드를 virtual 키워드로 선언해야 함
🔹 자식 클래스에서는 override 키워드를 사용해 메서드를 재정의함
🔹 부모 클래스의 메서드를 그대로 사용하지 않고, 자식 클래스에 맞게 동작을 변경할 때 사용
// 부모 클래스 (Animal)
class Animal
{
public string Name { get; set; }
// 가상(virtual) 메서드: 자식 클래스에서 오버라이딩 가능
public virtual void Speak()
{
Console.WriteLine("동물이 소리를 냅니다.");
}
}
// 자식 클래스 (Dog) - Animal 클래스를 상속받음
class Dog : Animal
{
// 부모 클래스의 Speak() 메서드를 오버라이딩
public override void Speak()
{
Console.WriteLine($"{Name}이(가) 멍멍 짖습니다.");
}
}
class Program
{
static void Main(string[] args)
{
// 부모 클래스의 인스턴스
Animal myAnimal = new Animal();
myAnimal.Name = "동물";
myAnimal.Speak(); // "동물이 소리를 냅니다."
// 자식 클래스의 인스턴스
Dog myDog = new Dog();
myDog.Name = "강아지";
myDog.Speak(); // "강아지이(가) 멍멍 짖습니다."
}
}
✅ 오버라이딩을 활용하는 이유
1️⃣ 다형성(Polymorphism) 지원 → 같은 Speak() 메서드를 호출해도 클래스에 따라 다르게 동작
2️⃣ 코드 재사용성 증가 → 부모 클래스의 기본 동작을 유지하면서 특정 부분만 변경 가능
3️⃣ 객체 지향적인 프로그래밍 가능 → 상속 관계를 활용한 유연한 설계
✅ 오버라이딩과 오버로딩 비교
구분 | 오버라이딩(Overriding) | 오버로딩(Overloading) |
개념 | 부모 클래스의 메서드를 자식 클래스에서 재정의 | 같은 이름의 메서드를 매개변수 타입/개수 다르게 정의 |
키워드 | virtual, override 사용 | 없음 (단순히 같은 이름의 메서드 추가) |
사용 예 | Speak()를 Dog에서 재정의 | Add(int a, int b)와 Add(double a, double b) |
📌업캐스팅(Upcasting)
✅ 업캐스팅(Upcasting)이란?
🔹 자식 클래스의 객체를 부모 클래스 타입으로 변환하는 것
🔹 암시적(Implicit) 변환 가능 → 별도의 형 변환 연산자 없이 자동 변환됨
🔹 데이터 손실 없이 안전한 변환
🔹 부모 클래스에서 정의된 멤버(속성, 메서드)만 접근 가능
🔹 자식 클래스의 고유 멤버에는 접근할 수 없음
✅ 업캐스팅을 활용하는 이유
1️⃣ 다형성(Polymorphism) 지원
- 부모 타입으로 여러 자식 객체를 관리할 수 있음
- 유지보수성과 확장성이 뛰어남
2️⃣ 코드의 일관성 유지
- 동일한 타입(부모)으로 처리하여 코드가 간결해짐
3️⃣ 컬렉션에서 활용 가능
- List<Animal> 같은 자료구조에서 다양한 자식 객체를 저장할 때 유용
// 부모 클래스 (Animal)
class Animal
{
public void Speak()
{
Console.WriteLine("동물이 소리를 내고 있습니다.");
}
}
// 자식 클래스 (Dog) - Animal 클래스를 상속받음
class Dog : Animal
{
public void Bark()
{
Console.WriteLine("멍멍!!");
}
}
class Program
{
static void Main(string[] args)
{
Dog myDog = new Dog(); // 자식 클래스 객체 생성
Animal myAnimal = myDog; // 업캐스팅 (Dog → Animal)
myAnimal.Speak(); // ✅ 가능 (부모 클래스의 메서드는 호출 가능)
// myAnimal.Bark(); // ❌ 오류 발생 (부모 타입으로 변환되어 자식 클래스의 메서드 사용 불가)
}
}
📌다운캐스팅(Downcasting)
🔹 부모 타입의 객체를 다시 자식 타입으로 변환하는 것
🔹 명시적(Explicit) 형 변환이 필요 → (자식클래스) 부모객체 형태로 변환해야 함
🔹 업캐스팅된 객체를 다시 원래의 자식 타입으로 돌려놓는 과정
🔹 잘못된 다운캐스팅은 런타임 오류(InvalidCastException)를 발생시킬 수 있음
class Animal
{
public void Speak()
{
Console.WriteLine("동물이 소리를 내고 있습니다.");
}
}
class Dog : Animal
{
public void Bark()
{
Console.WriteLine("멍멍!!");
}
}
class Program
{
static void Main(string[] args)
{
Animal myAnimal = new Dog(); // 업캐스팅 (Dog → Animal)
// 명시적 다운캐스팅 (부모 → 자식)
Dog myDog = (Dog)myAnimal;
myDog.Bark(); // 정상 실행 (멍멍!! 출력)
}
}
✅ 다운캐스팅이 필요한 경우
1️⃣ 자식 클래스의 고유 기능을 사용하고 싶을 때
- 업캐스팅하면 부모 클래스의 기능만 사용할 수 있으므로, 다시 자식 클래스로 변환해야 할 경우 필요
2️⃣ 다형성을 활용할 때
- 부모 타입 배열에 여러 자식 객체를 저장하고, 나중에 특정 타입으로 변환하여 추가 기능을 사용
✅ 안전한 다운캐스팅 방법
1️⃣ is 키워드 사용 (형 변환 가능 여부 확인)
if (myAnimal is Dog dog)
{
dog.Bark(); // 안전하게 Bark() 호출
}
else
{
Console.WriteLine("변환할 수 없는 타입입니다.");
}
2️⃣ as 키워드 사용 (형 변환 실패 시 null 반환)
Dog myDog = myAnimal as Dog;
if (myDog != null)
{
myDog.Bark(); // 안전하게 Bark() 호출
}
else
{
Console.WriteLine("변환할 수 없는 타입입니다.");
}
📌 base 키워드를 사용한 부모 클래스 호출하기
✅ base 키워드란?
- base 키워드는 자식 클래스에서 부모 클래스의 멤버(메서드, 속성, 생성자 등)를 호출할 때 사용합니다.
- 메서드를 오버라이딩했을 때, 부모 클래스의 원래 메서드도 실행하고 싶다면 base.메서드()를 호출하면 됩니다.
- 부모 클래스의 생성자를 호출할 때도 base 키워드를 사용할 수 있습니다.
✅ base 키워드가 필요한 이유
- 부모 클래스의 원래 기능을 유지하면서 추가적인 기능을 덧붙이고 싶을 때 사용합니다.
- 오버라이딩한 메서드에서 부모 클래스의 원래 동작을 포함하려면 base.메서드()를 호출해야 합니다.
class Parent
{
public virtual void ShowMessage()
{
Console.WriteLine("부모 클래스의 메세지");
}
}
class Child : Parent
{
public override void ShowMessage()
{
Console.WriteLine("자식 클래스의 메세지");
base.ShowMessage(); // 부모 클래스의 ShowMessage() 호출
}
}
class Program
{
static void Main(string[] args)
{
Child child = new Child();
child.ShowMessage();
}
}
📌 상속에서 protected 접근 제한자와 생성자 실행 순서
class Player
{
protected string Name; // 🔹 protected: 부모와 자식 클래스에서만 접근 가능
public Player()
{
Name = "플레이어";
Console.WriteLine("생성자입니다.");
}
public void Show()
{
Console.WriteLine(Name);
}
}
class Wizard : Player//자식 클래스
{
public Wizard()
{
Name = "마법사"; // 🔹 부모 클래스의 protected 멤버에 접근 가능
Console.WriteLine("자식 생성자입니다.");
}
}
class Program
{
static void Main(string[] args)
{
Player p = new Player();
p.Show();
Wizard w = new Wizard();
w.Show(); // 🔹 부모 생성자 -> 자식 생성자 순서로 실행됨
}
}
1️⃣ Player p = new Player();
- Player의 생성자 실행 → "생성자입니다." 출력.
- Show() 실행 → "플레이어" 출력.
2️⃣ Wizard w = new Wizard();
- 부모 클래스의 생성자 먼저 실행됨 → "생성자입니다." 출력.
- Wizard의 생성자 실행 → "자식 생성자입니다." 출력.
- Show() 실행 → "마법사" 출력
✅ 생성자 실행 순서 정리
✔ 자식 클래스의 생성자가 실행되기 전에 부모 클래스의 생성자가 먼저 실행됩니다.
✔ 이것은 상속 관계에서 부모의 초기화가 먼저 완료된 후, 자식이 추가적인 초기화를 진행하기 때문입니다.
✔ 이런 실행 순서는 base() 키워드를 통해 명시적으로 제어할 수도 있습니다.
'공부 > 부트캠프' 카테고리의 다른 글
[멋쟁이사자처럼부트캠프 Unity 게임개발 4기] 11일차: 델리게이트/이벤트/액션 (0) | 2025.03.07 |
---|---|
[멋쟁이사자처럼부트캠프 Unity게임개발 4기] 10일차 : 네임스페이스/인터페이스 (1) | 2025.03.06 |
[멋쟁이사자처럼부트캠프 Unity게임개발 4기] 8일차: 예외처리/리스트/배열리스트/스택/큐 (1) | 2025.03.04 |
[멋쟁이사자처럼부트캠프 Unity게임개발 4기] 7일차 : 클래스 + 캡슐화 (0) | 2025.02.28 |
[멋쟁이사자처럼부트캠프 Unity게임개발 4기] 6일차 : 함수(메서드), 구조체, 클래스 (1) | 2025.02.27 |