Post List

2018/01/25

C#__6.0 LINQ(링큐)

이번에는 LINQ 대해 알아보겠습니다. 링큐라고도 하지요!ㅎㅎ
링큐는 컬렉션을 편리하게 다루기 위한 목적으로 만들어진 질의(Query)언어입니다. 기능은 C#3.0 버전에서부터 탑재되었고, 덕분에 데이터를 찾고 병합하고 정렬하는 코드를 작성하는 짐을 상당 부분 내려놓을 있게 되었습니다.

LINQ Language Integrated Query 약어로, C# 언어에 통합된 데이터 질의 기능을 말하며, 데이터 질의 기능이란, 데이터에 대해 무언가를 물어본다는 말입니다. 기본적으로 질문은 아래 내용을 포함합니다.

From
어떤 데이터 집합에서 찾을 것인가?
Where
어떤 값의 데이터를 찾을 것인가?
Select
어떤 항목을 추출할 것인가?


코드를 보며 알아보겠습니다.

1
2
3
4
5
class Profile
{
    public string Name { get; set; }
    public int Height { get; set; }
}
cs

위와같이 선언된 클래스가 있다고 할때, 클래스를 기반해서 배열을 선언해보겠습니다.

1
2
3
4
5
6
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,
                        };
cs

이제 arrProfile 배열에서 Height 프로퍼티가 175 미만인 데이터만 골라 오름차순으로 정렬하는 과정을 링큐를 사용하지 않은 코드와 링큐를 사용한 코드를 비교해보겠습니다.

먼저 링큐를 사용하지 않은 코드입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<Profile> profile = new List<Profile>(); 
foreach(Profile data in arrProfile)
{
    // Height가 175미만인 데이터를 profile 리스트에 입력합니다.
    if (data.Height < 175)
        profile.Add(data);
}
profile.Sort(
            (data1, data2) =>
            {
                return data1.Height - data2.Height;
            });
cs

그다음 링큐를 사용한 코드입니다.
1
2
3
4
5
var profile = from      data in arrProfile          // arrProfile 안에 있는 각 데이터로부터
               where     data.Height < 175       // Height가 175 미만인 객체를 골라
               orderby   data.Height               // 키순으로
               select    data;                        // data를 추출합니다.
cs

네… 상당히 단순해졌습니다.. LINQ 이용하면 프로그래머들은 지루한 데이터 작업으로부터 많이 벗어날 있습니다.
LINQ 문법에 대해서 알아보았으니  from, where, orderby, select 대해 조금 자세히 알아보겠습니다.


먼저 from 대해 알아보겠습니다.
모든 링큐 쿼리식은 반드시 from절로 시작합니다. 우리는 리식의 대상이 데이터 원본과 데이터 원본 안에 들어 있는 요소 데이터를 나타내는 범위 변수를 from 절에서 지정해주어야 합니다.
이때 from 데이터 원본은 아무 형식이나 사용할 없고, IEnumerable<T> 인터페이스를 상속하는 형식어야만 합니다.
이전에 알아보았던 배열이나 컬렉션 객체들은 IEnumerable<T> 상속하기 때문에 모두 from절의 데이터 원본으로 사용이 가능합니다.
범위 변수는 쿼리 변수라고도 하는데, foreach문의 반복 변수를 생각하면 이해하기 쉽습니다foreach(int x in arr)에서 x 같은 변수 말입니다. 

from 범위변수 in 데이터원본(컬렉션or배열)
from절은 위 형식으로 사용합니다.

* 여기서 foreach 반복 변수와 다른점은 foreach문은에서는 데이터 원본으로부터 데이터를 담아내지만, from 범위변수는 실제로 데이터를 담지는 않습니다. 그래서 쿼리 외부에서 선언된 변수를 범위 변수로 사용할 없고, 범위 변수는 오로지 링큐 질의안에서만 통용되며, 질의가 실행될 어떤일이 일어날지를 묘사하기 위해 도입되었기 때문입니다.

두번째로 where 입니다.
Where 한마디로 필터 역할을 하는 연산자입니다. From절이 데이터 원본으로부터 뽑아낸 범위 변수가 가져야하는 조건을 where연산자에 매개 변수로 입력하면 링큐는 해당 조건에 부합하는 데이터만을 걸러냅니다.
아까 위에서 Height 175 미만인 data 골라냈던것처럼 말이죠.

세번째로 orderby입니다.
orderby 데이터의 정렬을 수행하는 연산자입니다. 위에서 Height 오름차순으로 정렬하기 위해 사용했었죠,

1
orderby   data.Height                 // 키순으로
cs

orderby 기본적으로 오름차순으로 데이터를 정렬하지만, 명확하게 오름차순으로 정렬한다는 사실을 알려주기위해 Ascending 키워드를 명시해주기도 합니다.
1
orderby   data.Height ascending       // 오름차순
cs

그럼 반대로 내림차순으로 정렬하고싶을때는 descending 키워드를 사용하면 됩니다.
1
orderby   data.Height descending     // 내림차순
cs

네번째 select 입니다.
select절은 최종 결과를 추출하는 퀴리식의 마침표와 같은 존재입니다. from절에서 데이터 원본으로부터 범위 변수를 뽑아내고 where절에서 범위 변수의 조건을 검사한 , 결과를 orderby절에서 정렬한 다음 select문을 이용해서 최종 결과를 추출해냅니다.


쿼리문에 대해서 알아보았으니 아까 위 예제코드에서 쿼리문의 반환값을 var 형식으로 받는걸 보셨을겁니다. C#컴파일러가 var형식을 링큐쿼리식이 반환할 결과 형식에 맞춰서 알아서 컴파일해주기는 하지만, 실제로 var 어떤 형식으로 치환되는지를 프로그래머로써 알아둘 필요가 있습니다.

링큐의 질의 결과는 IEnumerable<T> 반환되는데, 이때 형식 매개 변수 T select문에 의해 결정됩니다.
아까 쿼리식에서 Height 175 미만인 Profile객체를 골라내는데, 결과는 IEnumerable<Profile> 형식이 됩니다.
만약 select문에서 Profile객체 전체가 아닌 Name 프로퍼티만 추출하게 된다면 profile IEnumerable<string> 형식으로 컴파일 됩니다.

1
2
3
4
 var profile = from      data in arrProfile           // arrProfile 안에 있는 각 데이터로부터
                where     data.Height < 175           // Height가 175 미만인 객체를 골라
                orderby   data.Height                  // 키순으로
                select    data.Name;                   // data.Name 프로퍼티를 추출합니다.
cs

또한 select문은 무명 형식을 이용해서 다음과 같이 새로운 형식을 만들어낼 수도 있습니다.

1
2
3
4
var profile = from data in arrProfile               // arrProfile 안에 있는 각 데이터로부터
               where data.Height < 175              // Height가 175 미만인 객체를 골라
               orderby data.Height                   // 키순으로
               select new { Name = data.Name, InchHeight = data.Height * 0.393 };
cs