LINQ : Language INtegrated Query, C#에 도입된 통합된 데이터 질의 기능
SQL 언어랑 비슷하며 데이터를 다루기 위한 문법이다.
Name과 Height를 프로퍼티로 가지는 Profile 클래스가 있다하자.
다음과 같은 배열에서 Height가 175 미만인 인스턴스를 오름차순으로 정렬하고 싶을때 만족하는 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}
};
List<Profile> profiles = new List<Profile>();
foreach (Profile profile in arrProfile)
{
if (profile.Height < 175)
profiles.Add(profile);
}
profiles.Sort((profile1, profile2) =>
{
return profile1.Height - profile2.Height;
});
foreach (var profile in profiles)
Console.WriteLine("{0}, {1}", profile.Name, profile.Height);
C# LINQ를 이용하면 SQL 언어와 비슷하게 arrProfile을 테이블로 간주하고 각 테이블의 행 Profile을 찾는 쿼리식을 짤 수 있다.
var profiles_find_by_linq = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select profile;
LINQ의 기본 : from. where, orderby, select
- from : 참조할 인스턴스의 범위를 말한다. 반드시 IEnuerable<T>를 상속하는 클래스이어야한다. (ex : profile)
- where : 참조하는 인스턴스 중 조건에 부합하는 인스턴스 데이터만 가져온다.
- orderby : 정렬을 수행한다. 정렬을 수행할 기준을 인수로 입력하면 된다.
- select : 최종 결과를 추출한다. 참조한 인스턴스의 속성을 가지고 새로운 형식을 만들어 낼 수 있으며 최종 반환되는 형식은 IEnumerable<T>이다. 이때 T는 실제 클래스가 될 수도 있고 새로운 익명의 형식이 될 수 도 있다.
// 새로운 형식을 만들어 낼 수 도 있다.
var new_profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select new { Name = profile.Name, InchHeight = profile.Height * 0.393 };
group by
group A by B into C
A에는 from 절에서 선택한 범위 변수를 B에는 분류 기준, C에는 그룹 변수를 위치시킨다. C라는 그룹 키워드를 통해 select에서 새로운 데이터를 정의할 수 있다.
var listProfile = from profile in arrProfile
group profile by profile.Height < 175 into g
select new { GroupKey = g.Key, Profiles = g };
내부 조인과 외부 조인
내부 조인 (Inner Join)
내부 조인은 두 테이블에서 같은 값을 지닌 행을 모아 새로운 테이블을 만든다.
from a in A
join b in B on a.PropertyA equals b.PropertyB
arrProfile 테이블과 arrProduct 테이블의 인덱스 중 profile.Name == product.Star가 같은 인덱스만 모아 새로운 테이블을 만든다.
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}
};
Product[] arrProduct =
{
new Product(){ Title = "비트", Star = "정우성"},
new Product(){ Title = "CF 다수", Star = "김태희"},
new Product() { Title= "아이리스", Star = "김태희"},
new Product() { Title= "모래시계", Star = "고현정"},
new Product() { Title= "Solo 예찬", Star = "이문세"}
};
var innerJoinResult = from profile in arrProfile
join product in arrProduct on profile.Name equals product.Star
select new
{
Name = profile.Name,
Work = product.Title,
Height = profile.Height
};
외부 조인 (Outer Join)
내부 조인과 마찬가지로 두 테이블에서 같은 비교값을 가지는 모든 행을 가져오며 내부 조인과 다르게 항상 왼쪽 테이블을 기준으로 일치하는 데이터가 있다면 가져오고 없다면 빈값으로 채운다.
실제 코드는 내부 조인을 한 코드에서 결과 테이블에 빈 값을 채우는 DefaultIfEmpty 연산을 거친다.
var outerJoinResult = from profile in arrProfile
join product in arrProduct on profile.Name equals product.Star into ps
from product in ps.DefaultIfEmpty(new Product() { Title = "그런거 없음" })
select new
{
Name = profile.Name,
Work = product.Title,
Height = profile.Height
};
LINQ 쿼리식의 컴파일러 해석
LINQ 쿼리식은 System.Linq 네임스페이스에 정의되어 있는 IEnumerable<T>의 확장 메서드를 이용한 것이다. 다음 arrProfiles는 배열이며 IEnumerable<T>를 상속한 형식이다. 따라서 LINQ로 작성된 코드는 일대일로 대응되는 확장 메서드(표준 연산자)가 있다.
var linq_query_profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select new { Name = profile.Name, InchHeight = profile.Height * 0.393 };
var linq_method_profiles = arrProfile.Where(profile => profile.Height < 175)
.OrderBy(profile => profile.Height)
.Select(profile =>
new { Name = profile.Name, InchHeight = profile.Height * 0.393 });
50개가 넘는 표준 연산자 중 LINQ 쿼리식으로 지원하는 것은 11개 뿐이다. 그밖에 많은 메서드가 있으며, 정렬, 집합, 필터링, 수량, 투영, 그룹, 형식변환, 집계등 자주 활용되는 메서드가 있다.
ex) 평균 키를 구하기
var profiles_test = from profile in arrProfile
where profile.Height < 180
select profile;
double Average = profiles_test.Average(profile => profile.Height);
ex) 키의 최솟값, 최댓값
double MinVal = profiles_test.Min(profile => profile.Height);
double MaxVal = profiles_test.Max(profile => profile.Height);
ex) 정우성 이름을 가진 요소가 있는지
bool profiles_named_jung = profiles_test.Any(profile => profile.Name.Contains("정우성"));
'C# > C# 기본' 카테고리의 다른 글
[C#] dynamic 타입 (0) | 2021.10.19 |
---|---|
[C#] 리플렉션과 애트리뷰트 (0) | 2021.10.19 |
[C#] 람다식 (0) | 2021.10.15 |
[C#] 대리자와 이벤트 (0) | 2021.10.15 |
[C#] Generic 프로그래밍 / 예외 처리 (0) | 2021.10.12 |