[C#] LINQ의 사용법에 관하여
개요
LINQ(Language Integrated Query)는 데이터를 질의하고 조작하기 위해 일관되고, 직관된 문법
으로 C# 언어에 통합된 쿼리 기능입니다.
데이터 소스(컬렉션, 데이터베이스, XML 등)에서 데이터를 효율적으로 검색, 필터링, 정렬, 그룹화하는 등의 작업을 수행할 수 있도록 해줍니다. LINQ를 사용하면 SQL과 유사한 구문으로 코드를 작성하여 데이터를 쉽게 조작할 수 있으며, 코드의 가독성과 유지 보수성을 향상시키는 데 도움이 됩니다.
특징
- 통합된 쿼리: C# 언어 내에서 직접 쿼리를 작성할 수 있어 코드의 가독성이 높아집니다.
- 다양한 데이터 소스 지원: 컬렉션(List, Array 등), 데이터베이스(Entity Framework 등), XML 등 다양한 데이터 소스에서 쿼리할 수 있습니다.
- 표준 쿼리 연산자 제공:
Where
,Select
,OrderBy
,GroupBy
등 표준 쿼리 연산자를 사용하여 데이터를 조작할 수 있습니다. - 메서드 구문과 쿼리 구문: 메서드 체이닝 방식의 메서드 구문과 SQL과 유사한 쿼리 구문 두 가지 방식을 모두 사용할 수 있습니다.
- 지연된 실행(Deferred Execution): 쿼리가 실제로 실행되는 시점을 지연시켜 성능을 향상시킬 수 있습니다.
- 강력한 타입 안정성: 컴파일 시점에 쿼리의 오류를 검사하여 런타임 오류를 줄일 수 있습니다.
장단점
장점
- 데이터 조작 코드를 간결하고 가독성 높게 작성할 수 있습니다.
- 다양한 데이터 소스를 일관된 방식으로 처리할 수 있습니다.
- 코드 생산성과 유지 보수성을 향상시킵니다.
- 타입 안정성을 제공하여 오류를 사전에 방지할 수 있습니다.
단점
- 처음에는 쿼리 구문이나 메서드 구문에 익숙해지는 데 시간이 걸릴 수 있습니다.
- 복잡한 쿼리의 경우 성능 문제가 발생할 수 있으므로 최적화가 필요할 수 있습니다.
- 지연된 실행으로 인해 디버깅이 다소 어려울 수 있습니다.
주의점
- 네임스페이스 추가: LINQ를 사용하기 전에 using System.Linq; 네임스페이스를 추가해야 합니다.
- 지연된 실행 이해: 쿼리가 언제 실행되는지 이해하고 있어야 성능 문제를 방지할 수 있습니다. ToList()나 ToArray() 등의 메서드를 사용하여 즉시 실행할 수 있습니다.
- 성능 고려: 복잡한 쿼리는 성능에 영향을 줄 수 있으므로 인덱싱, 필터링 순서 등을 고려하여 최적화해야 합니다.
- Null 처리: 데이터 소스에 null 값이 포함되어 있을 경우 NullReferenceException이 발생할 수 있으므로 null 체크를 수행해야 합니다.
메서드 구문과 쿼리 구문 비교
메서드 구문:
var evenNumbers = numbers.Where(n => n % 2 == 0).OrderBy(n => n);
쿼리 구문:
var evenNumbers = from n in numbers
where n % 2 == 0
orderby n
select n;
사용법
- 네임스페이스 추가: 스크립트 상단에 using System.Linq;를 추가합니다.
- 데이터 소스 준비: 쿼리할 데이터 소스(컬렉션, 배열 등)를 준비합니다.
- 쿼리 작성: 메서드 구문 또는 쿼리 구문을 사용하여 쿼리를 작성합니다
LINQ 식
[from] : 어떤 데이터에서 원하는 값을 추출할 것인지를 정합니다.
IEnumerable<Item> _items = from item in items select item;
foreach (var _item in _items) _item.Print();
[Where] : 조건을 만족하는 요소만 필터링합니다.
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbers = numbers.Where(n => n % 2 == 0); // 짝수만 필터링
[Select] : 요소를 변환합니다.
var squaredNumbers = numbers.Select(n => n * n); // 각 숫자를 제곱
[OrderBy] : 요소를 정렬합니다.
var sortedNumbers = numbers.OrderBy(n => n); // 오름차순 정렬
[GroupBy] : 요소를 그룹화합니다.
var groupedNumbers = numbers.GroupBy(n => n % 2); // 짝수/홀수로 그룹화
[FirstOrDefault] : 조건에 맞는 첫 번째 요소를 반환하고, 없으면 기본값을 반환합니다.
int firstNumberGreaterThan10 = numbers.FirstOrDefault(n => n > 10); // 10보다 큰 첫 번째 숫자 (없으면 0 반환)
LINQ 함수
[Reverse] : 내용물을 뒤집는다.(반전시킨다.)
_items = items.Reverse();
[Distince] : 중복 제거
_items = items4.Distinct();
[Except] : 차집합 items - items2
_items = items.Except(items2);
[Intersect] : 교집합 items ∩ items2
_items = items.Intersect(items2);
[Union] : 합집합 items ∪ items2
_items = items.Union(items2);
[Where]
- 조건이 true인 값을 선택
- 다른 조건 함수와 다르게 IEnumerable
타입 리턴
// Where : 조건이 true인 값을 선택
_items = items.Where(x => x.code > 1);
[All] : 모두 true -> true 반환
bool b = items.All(x => x.code > 0);
print(b);
[Any] : 하나라도 true -> true 반환
bool b = items.Any(x => x.code > 3);
print(b);
[Contains] : 특정 원소 포함 여부 반환
bool b = items.Contains(new Item(3, "banana"), new ItemComparer());
print(b);
[Select]
- 값을 추출해 IEnumerable
타입 리턴 - Array/List 등의 타입을 IEnumerable 타입으로 변환
- new {}를 통한 익명 형식 적용 가능
IEnumerable<int> codes = items.Select(x => x.code);
foreach (int code in codes)
print(code);
var _customItems = items.Select(x => new { Name = x.name, CodeAdd = x.code + 1 });
foreach (var _customItem in _customItems)
print(_customItem);
[SelectMany] : 두 IEnumerable의 모든 조합을 만듬
int[] number = new int[] { 10, 20 };
string[] animals = new string[] { "cat", "dog", "donkey" };
var mix = number.SelectMany(num => animals, (n, a) => new { n, a });
foreach (var a in mix)
print(a);
[Skip] : 건너뛰고 인자 인덱스부터 시작
_items = items.Skip(2);
[SkipWhile]
- 순서대로 정렬됐을 때, true일 동안 스킵
- {1, 2, 3, 4}에서 3보다 작을 동안을 스킵, 3, 4만 나옴
int[] number = new int[] { 10, 20 }; string[] animals = new string[] { "cat", "dog", "donkey" }; var mix = number.SelectMany(num => animals, (n, a) => new { n, a }); foreach (var a in mix) print(a);
[Take] : 앞에서 부터 개수만큼 가져오기
_items = items.Take(2);
[TakeWhile]
- 순서대로 정렬됐을 때, true일 동안 가져오기
- {1, 2, 3, 4}에서 3보다 작을 동안 가져오기, 1, 2만 나옴
_items = items.OrderBy(x => x.code).TakeWhile(x => x.code < 3);