LINQ는 컬렉션을 편리하게 다루기 위한 목적으로 만들어진 기능이며, C# 3.0 버전부터 탑재되었습니다. LINQ 덕분에 C# 프로그래머는 데이터를 찾고, 병합하고, 정렬하는 코드를 작성하는 부담을 상당히 줄일 수 있게 되었습니다. 마치 데이터베이스의 쿼리 언어(SQL)처럼 C# 코드 안에서 데이터를 질의(Query)하고 조작할 수 있게 해주는 것입니다.
LINQ는 데이터를 쿼리하기 위해 여러 가지 절(clause)을 사용합니다. 기본적인 쿼리 구문은 다음과 같은 형태로 이루어집니다.
from
→ where
→ orderby
→ select
각 절의 역할은 다음과 같습니다.
from
절: 쿼리할 데이터 원본과 범위 변수(range variable)를 지정합니다. 범위 변수는 데이터 원본의 각 요소를 대표하며, 이어지는 절들에서 이 변수를 사용하여 요소를 참조합니다.
예: from profile in arrProfile
(arrProfile이라는 데이터 원본에서 각 요소를 profile이라는 이름으로 참조)
where
절: 데이터 원본에서 특정 조건을 만족하는 요소만 걸러내는 필터링(Filtering) 역할을 합니다. where
절 뒤에는 불리언(bool) 값을 반환하는 조건식이 옵니다.
예: where profile.Height < 175
(키가 175 미만인 profile만 선택)
orderby
절: 필터링된 데이터를 특정 기준에 따라 정렬(Sorting)합니다. 오름차순(ascending
) 또는 내림차순(descending
)으로 정렬할 수 있으며, 기본값은 오름차순입니다.
예: orderby profile.Height
(Height를 기준으로 오름차순 정렬)
예: orderby profile.Height descending
(Height를 기준으로 내림차순 정렬)
select
절: 쿼리 결과를 프로젝션(Projection)합니다. 즉, 쿼리 결과로 어떤 데이터를 반환할지 정의합니다. 데이터 원본의 요소를 그대로 반환하거나, 요소의 일부만 선택하거나, 여러 요소를 조합하여 새로운 형태의 익명 형식(Anonymous Type)으로 반환할 수 있습니다.
예: select profile
(조건에 맞는 profile 객체 자체를 반환)
예: select profile.Name
(조건에 맞는 profile의 Name 속성만 반환)
예: select new { Name = profile.Name, InchHeight = profile.Height * 0.393 }
(Name과 계산된 InchHeight를 포함하는 새로운 익명 형식 객체 반환)
다음은 기본적인 LINQ 쿼리 구문의 예시 코드입니다.
// Profile 클래스 정의 (이름과 키)
class Profile
{
public string Name { get; set; }
public int Height { get; set; }
}
// 데이터 원본 (Profile 배열)
Profile[] arrProfile =
{
new Profile(){Name="정우성", Height=186},
new Profile(){Name="김태희", Height=158},
new Profile(){Name="고현정", Height=172},
new Profile(){Name="이문세", Height=178},
new Profile(){Name="하하", Height=171}
};
// LINQ 쿼리
var profiles = from profile in arrProfile // 데이터 원본과 범위 변수
where profile.Height < 175 // 키가 175 미만인 요소 필터링
orderby profile.Height // 키를 기준으로 오름차순 정렬
select profile; // 필터링되고 정렬된 profile 객체 선택
// 쿼리 결과 출력 (예시)
foreach (var profile in profiles)
{
Console.WriteLine($"이름:{profile.Name}, 키:{profile.Height}");
}
위 예시에서는 키가 175 미만인 Profile 객체들을 키 순서대로 정렬하여 가져옵니다. select new { ... }
구문을 사용하면 원하는 속성만 선택하거나 계산된 속성을 추가하여 새로운 형태의 결과를 만들 수 있습니다.
LINQ 쿼리는 쿼리 구문 외에 메서드 구문으로도 작성할 수 있습니다. 이는 Where()
, OrderBy()
, Select()
등과 같은 확장 메서드를 체인처럼 연결하여 사용합니다. 쿼리 구문과 메서드 구문은 서로 변환 가능하며 동일한 결과를 생성합니다. 많은 경우 컴파일러가 쿼리 구문을 메서드 구문으로 변환합니다.
메서드 구문 예시:
var profiles = arrProfile.Where(profile => profile.Height < 175) // Where 확장 메서드 사용
.OrderBy(profile => profile.Height) // OrderBy 확장 메서드 사용
.Select(profile => new { Name = profile.Name, InchHeight = profile.Height * 0.393 }); // Select 확장 메서드 사용
메서드 구문에서는 람다식(Lambda Expression)을 활용하여 조건을 지정하거나 데이터를 선택합니다.
LINQ는 기본적인 필터링, 정렬, 프로젝션 외에도 다양한 작업을 수행할 수 있습니다.
여러 데이터 원본 질의하기 (Nested from): from
절을 중첩하여 사용하여 여러 컬렉션의 데이터를 조합하여 쿼리할 수 있습니다.
예: 각 반(Class)의 학생(Score) 점수 중 60점 미만인 점수만 가져오기
var classes = from c in arrClass // 첫 번째 데이터 원본 (반)
from s in c.Score // 두 번째 데이터 원본 (점수 배열)
where s < 60 // 점수가 60 미만인 경우 필터링
select new { c.Name, Lowest = s }; // 반 이름과 점수 선택
데이터 분류하기 (group by): 데이터를 특정 기준에 따라 그룹핑(Grouping)할 수 있습니다. group by
절의 결과는 IGrouping<TKey, TElement>
형식으로 반환되며, 각 그룹은 키와 해당 그룹에 속하는 요소들의 컬렉션을 가집니다. into
키워드를 사용하여 그룹 결과를 새로운 범위 변수로 가져와 추가 작업을 수행할 수 있습니다.
예: Profile 데이터를 Height < 175 기준에 따라 그룹핑하기
var listProfile = from profile in arrProfile // 데이터 원본
group profile by profile.Height < 175 into g // 키가 175 미만 여부로 그룹핑하고 결과를 g로
select new { GroupKey = g.Key, Profiles = g }; // 그룹 키와 해당 그룹의 Profile들 선택
데이터 원본 연결하기 (join): 여러 데이터 원본을 특정 조건에 따라 연결하여 데이터를 가져올 수 있습니다. 이는 데이터베이스의 조인 연산과 유사합니다. join
절 뒤에 연결할 데이터 원본, 내부 컬렉션의 범위 변수, 조인 조건 (equals
사용)이 옵니다. 외부 조인도 지원하며, into
와 DefaultIfEmpty()
를 함께 사용하여 구현합니다.
예: arrProfile과 arrProduct 데이터를 Name과 Star 속성을 기준으로 연결하기
var listProfile = from profile in arrProfile // 외부 데이터 원본
join product in arrProduct on profile.Name equals product.Star into ps // 내부 데이터 원본과 조인 조건, 결과를 ps로
from product in ps.DefaultIfEmpty(new Product(){Title="없음"}) // 외부 조인을 위한 부분
select new // 결과 형태 정의
{
Name = profile.Name,
Work = product.Title,
Height = profile.Height
};
LINQ는 내부 조인(Inner Join), 왼쪽 외부 조인(Left Outer Join), 오른쪽 외부 조인(Right Outer Join), 완전 외부 조인(Full Outer Join)을 지원하지만, 외부 조인 방식은 SQL보다 조금 아쉬운 부분이 있을 수 있습니다.
LINQ는 위에서 살펴본 Where
, OrderBy
, Select
, GroupBy
, Join
과 같은 것들을 포함하여 다양한 작업을 수행하는 표준 쿼리 연산자들을 제공합니다. 이들은 대부분 System.Linq
네임스페이스에 정의된 확장 메서드 형태로 제공됩니다.
또한, 데이터 집계(Aggregation)를 위한 연산자들도 있습니다.
Count()
: 요소의 개수를 계산합니다.Average()
: 숫자의 평균을 계산합니다.Min()
: 최소값을 찾습니다.Max()
: 최대값을 찾습니다.