LINQ란?
C#에서 데이터 쿼리를 위한 통합 프로그래밍 모델로, 다양한 데이터 소스(컬렉션, XML, 데이터베이스 등)에
대해 일관된 방식으로 쿼리를 작성할 수 있게 해주는 기능
예를 들어, 도서관에서 책 찾기를 생각하면 쉽다
"2000년 이후에 출간된 소설책만 찾아줘"
"가격이 2만원 이하인 책들을 가격 순으로 정렬해줘"
"과학 분야 책들의 평균 가격을 계산해줘"
복잡한 for문이나 if문을 여러 번 사용하여 찾을 수도 있지만,
LINQ를 사용하면 더욱 쉽고 직관적으로 처리할 수 있다.
예시
// 체력이 50% 이하인 적 캐릭터들 찾기
var weakEnemies = enemies.Where(enemy => enemy.health < 50);
// 점수가 높은 순서대로 플레이어 정렬하기
var topPlayers = players.OrderByDescending(player => player.score);
쉽게 말해 "데이터를 검색하고 정리하는 만능 도구" 정도로 이해하면 쉽다
LINQ의 문법
LINQ의 사용할 때는 2가지의 방법이 있다
1. 쿼리구문 ( Query Syntax )
var result = from item in items
where item.price < 1000
select item;
특징
영어 문장처럼 읽히는 방식이다
"items에서 가격이 1000원 미만인 것을 선택해줘" 라고 읽는 느낌
SQL문과 비슷한 형태라서 데이터베이스 개발자들이 친숙하게 느낀다
2. 메서드 구문 ( Method Syntax )
var result = items.Where(item => item.price < 1000);
특징
메서드를 체인처럼 연결하는 방식이다
점(.)으로 메서드들을 이어붙이다
C# 개발자들이 더 선호하는 방식이다
실제 예시
게임에서 체력이 50이상이고 레벨순으로 정렬된 플레이어 찾기
// 1. 쿼리 구문
var players1 = from player in allPlayers
where player.health >= 50
orderby player.level
select player;
// 2. 메서드 구문
var players2 = allPlayers
.Where(player => player.health >= 50)
.OrderBy(player => player.level);
두 방식 모두 같은 결과를 만들어내며, 개인의 취향에 따라 선택하면 된다
단, 메서드 구문이 더 간결하고 더 많은 기능을 사용할 수 있으며, 디버깅이 더 쉽다는 장점이 있다
LINQ의 실행 방식
LINQ에는 2가지 실행 방식이 있다
1. 지연 실행 ( 기본 동작 )
// 이 시점에서는 실제로 필터링이 실행되지 않음
var query = numbers
.Where(x => x > 5)
.Select(x => x * 2);
2. 즉시 실행
// ToList()를 호출하는 순간 실제로 필터링이 실행됨
var result = numbers
.Where(x => x > 5)
.Select(x => x * 2)
.ToList();
실생활에 비유하자면
지연 실행은 "장보기 목록 작성"
즉시 실행은 "실제로 장 보러 가는 것"
LINQ의 지연 실행에서 중요한 점
실제로 결과값이 필요한 시점에 실행된다는 것
즉, 정의만 하고 실제로 사용하지 않으면 영원히 실행되지 않는다.
실제 데이터가 필요한 시점 (foreach, ToList(), First() 등을 호출할 때) 에 실행되는 것이다
예시
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// 지연 실행 확인용 코드
var query = numbers.Where(x => {
Console.WriteLine($"체크 중: {x}"); // 이 줄은 아직 실행 안됨
return x > 2;
});
Console.WriteLine("쿼리 만들기 완료"); // 이 줄만 실행됨
// 아무것도 하지 않으면 위의 Where 필터링은 실행되지 않음
// 실제로 데이터가 필요한 시점(foreach 등)에서 실행됨
foreach(var num in query) {
// 이 시점에서 "체크 중: ..." 메시지들이 출력됨
Console.WriteLine($"결과: {num}");
}
이것은 LINQ의 지연 실행에서 굉장히 주의해야 할 중요한 부분이다
예시
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// 쿼리 정의
var query = numbers.Where(x => x > 2);
// numbers 리스트 수정
numbers.Add(6);
numbers.Remove(3);
// 이 시점에서 쿼리 실행
foreach(var num in query)
{
Console.WriteLine(num);
}
// 결과: 4, 5, 6
실행한 시점에서의 값을 가져오기에 데이터를 잃어버리거나
내가 원하는 값이 아닐 수 있다는 점이다
이러한 상황을 방지하려면?
즉시 실행으로 변환
// 쿼리 정의하자마자 List로 변환
var result = numbers.Where(x => x > 2).ToList();
// numbers 리스트를 수정해도 result는 영향 받지 않음
numbers.Add(6);
numbers.Remove(3);
원본 데이터 복사 후 사용
// 원본 데이터 복사
var numbersCopy = new List<int>(numbers);
var query = numbersCopy.Where(x => x > 2);
// 원본 numbers를 수정해도 query는 영향 받지 않음
numbers.Add(6);
지연 실행의 장점
- 필요한 시점까지 실행을 미뤄서 성능 향상
- 쿼리를 여러 번 재사용 가능
- 메모리 효율성 향상
즉시 실행이 필요한 주요 메서드
- ToList()
- ToArray()
- ToDictionary()
- Count()
- Sum()
- First()
- Last()
- Max()
- Min()
LINQ의 메서드
필터링 (Filtering) 메서드
.Where() // 조건에 맞는 요소 선택
.Where(x => x.age > 20) // 나이가 20살 초과인 플레이어 선택
.OfType<T>() // 특정 타입의 요소만 선택
.OfType<Enemy>() // Enemy 타입의 객체만 선택
.Skip() // 지정된 수만큼 요소 건너뛰기
.Skip(5) // 처음 5개의 요소를 건너뛰고 나머지 선택
.Take() // 지정된 수만큼 요소 가져오기
.Take(3) // 처음 3개의 요소만 선택
.TakeWhile() // 조건이 참인 동안만 요소 가져오기
.TakeWhile(x => x.score > 0) // 점수가 0보다 큰 동안만 요소 선택
.SkipWhile() // 조건이 참인 동안 요소 건너뛰기
.SkipWhile(x => x.score < 50) // 점수가 50 미만인 요소들을 건너뛰기
정렬 (Ordering) 메서드
.OrderBy() // 오름차순 정렬
.OrderBy(x => x.name) // 이름을 알파벳 순으로 정렬
.OrderByDescending() // 내림차순 정렬
.OrderByDescending(x => x.score) // 점수를 높은 순으로 정렬
.ThenBy() // 2차 오름차순 정렬
.ThenBy(x => x.age) // 같은 이름일 경우 나이순으로 정렬
.ThenByDescending() // 2차 내림차순 정렬
.ThenByDescending(x => x.level) // 같은 점수일 경우 레벨 높은순으로 정렬
그룹화 (Grouping) 메서드
.GroupBy() // 특정 속성으로 그룹화
.GroupBy(x => x.category) // 카테고리별로 그룹 만들기
.ToLookup() // 즉시 그룹화하여 조회용 형태로 변환
.ToLookup(x => x.type) // 타입별로 즉시 그룹화하여 조회 가능한 형태로 변환
집계 (Aggregation) 메서드
.Count() // 요소 개수 세기
.Count(x => x.isActive) // 활성화된 항목의 개수 세기
.Sum() // 합계 구하기
.Sum(x => x.value) // 모든 값의 합계 구하기
.Average() // 평균 구하기
.Average(x => x.score) // 모든 점수의 평균 구하기
.Max() // 최대값 구하기
.Max(x => x.height) // 가장 큰 키 값 찾기
.Min() // 최소값 구하기
.Min(x => x.height) // 가장 작은 키 값 찾기
투영 (Projection) 메서드
.Select() // 특정 속성 선택
.Select(x => x.name) // 이름만 선택하여 새로운 컬렉션 만들기
.SelectMany() // 중첩 컬렉션 펼치기
.SelectMany(x => x.items) // 각 플레이어가 가진 아이템들을 하나의 컬렉션으로 펼치기
집합 (Set) 메서드
.Distinct() // 중복 제거
.Distinct() // 중복된 요소들을 제거하고 유일한 값만 선택
.Union() // 합집합 (중복 제거)
.Union(secondList) // 두 리스트를 합치고 중복 제거
.Intersect() // 교집합
.Intersect(secondList) // 두 리스트에서 공통된 요소만 선택
.Except() // 차집합
.Except(secondList) // 첫 번째 리스트에서 두 번째 리스트에 있는 요소 제거
요소 접근 메서드
.First() // 첫 요소 가져오기
.First(x => x.id == 1) // ID가 1인 첫 번째 요소 가져오기
.Last() // 마지막 요소 가져오기
.Last(x => x.active) // 활성화된 마지막 요소 가져오기
.Single() // 단일 요소 가져오기
.Single(x => x.id == 5) // ID가 5인 요소 하나만 가져오기 (여러 개면 에러)
.ElementAt() // 특정 위치 요소 가져오기
.ElementAt(3) // 네 번째 위치의 요소 가져오기
변환 메서드
.ToList() // List로 변환
.ToList() // IEnumerable을 List로 변환
.ToArray() // 배열로 변환
.ToArray() // IEnumerable을 배열로 변환
.ToDictionary() // Dictionary로 변환
.ToDictionary(x => x.id) // ID를 키로 하는 Dictionary로 변환
판단 메서드
.Any() // 요소 존재 여부 확인
.Any(x => x.price > 1000) // 가격이 1000 초과인 요소가 있는지 확인
.All() // 모든 요소 조건 만족 여부
.All(x => x.score >= 0) // 모든 점수가 0 이상인지 확인
.Contains() // 특정 요소 포함 여부
.Contains(item) // 특정 아이템이 리스트에 있는지 확인
실제 활용 예시
활성화 된 적 찾기
var activeEnemies = gameObjects
.Where(enemy => enemy.activeInHierarchy)
.ToList();
가까운 아이템 찾기
var nearbyItems = items
.Where(item => Vector3.Distance(player.position, item.position) < radius)
.OrderBy(item => Vector3.Distance(player.position, item.position))
.ToList();
특정 태그를 가진 오브젝트 그룹화
var groupedObjects = gameObjects
.GroupBy(obj => obj.tag)
.ToDictionary(g => g.Key, g => g.ToList());
※ 디버깅 TIP
// 중간 결과 확인
var result = collection
.Where(x => x.value > 10)
.Select(x => x.name)
.ToList() // 중간 결과를 확인하기 위해 실체화
.Where(name => name.StartsWith("A"));
'유니티 > 문법' 카테고리의 다른 글
[Unity] 유니티 C#) Casting ( 형변환 ) (0) | 2025.01.16 |
---|---|
[Unity] 유니티 C#) try-catch문 ( 예외처리 ) (1) | 2025.01.15 |
[Unity] 유니티 C#) 전처리문 (Preprocessor Directives) (0) | 2025.01.13 |
[Unity] 유니티 C#) Event ( 이벤트 ), Action ( 액션 ) (0) | 2025.01.08 |
[Unity] 유니티 C#) Delegate ( 델리게이트 ), Anonymous Method (익명 메서드) ,Lambda ( 람다 ) (0) | 2025.01.07 |