삽질의 현장/- .NET

#066_닷넷(.NET)_.Net Framework 기본 - LINQ의 역할 (사용자 정의형)

shovelman 2015. 11. 6. 21:24


안녕하세요 삽잡이입니다.


이번 시간에는 사용자 정의형에 대한 LINQ에 대해서 좀 알아보려고합니다.



사용자 정의 타입 또한 LINQ가 가능합니다.

또한, where 즉, 조건 절에서 결과치가 bool 형식이라면 

몇 개의 조건을 줘도 상관없습니다.


제네릭 컬렉션의 경우 모두 IEnumerable<T> 형식을 상속받습니다.

따라서, LINQ의 동작에 대해서 걱정할 것이 없습니다.

하지만, 기본적으로 IEumerable<T> 형식을 지원하는 LINQ에서

비 제네릭 컬렉션의 경우 제네릭 이전에 나온 인터페이스로써 

꼼수(?)를 써야 LINQ를 사용할 수 있게 됩니다.


그 꼼수는 바로 OfType() 이라는 호환 가능한 확장 메서드를 사용하는 것입니다.



내부적으로 ArrayList는 모두 다 저장할 수 있는 object 타입입니다.

그리고 이를 각각 맞는 타입으로 변환해줄 때 OfType() 확장 메서드를 사용하지요.

이처럼 OfType<타입>을 명시해줌으로써

LINQ 에서도 호환가능한 타입으로 변환이 가능합니다.


또한, OfType<>() 메서드를 사용해서 필터링도 가능합니다.

형식이 아닌 놈을 제외하고 집합으로 만들 수 있다 이겁니다.



재미있는 것은 지금 껏 우리는 LINQ를 때려서 같은 타입의 집합만을 얻었습니다.


그런데 지금부터는,

사용자 타입에 LINQ를 때려 내가 원하는 타입을 만들어보겠다 이겁니다.


내가 알고싶은 집합이 만약, 위의 예제에서 name과 made라면?

Sap() 집합이라고 할 수 있을까요?

아닙니다... 기존의 Sap()은 두 가지의 형식만 있는게 아니지 않습니까...



Sap에 있는 하나의 필드에 관한 집합을 얻는것은 쉽습니다.

select 절을 사용하면 되지요...

같은 타입만 추출하여 모은다면 select 절은 필요없습니다.

select는 다른 집합을 얻기 위해서 필요합니다.

LINQ를 때릴 때 Sap 집합을 대상으로 질의를 던지고

Service의 집합이 string 혹은 int 등의 집합으로 만들어 받으면 되기 때문입니다.


자... 지금까지는 전혀 다른 타입을 얻되 우리가 알고 있는 타입을 얻는 방법까지 알아봤습니다.


그런데 우리는 아직 name 과 made라는 

'전혀 모르는 타입'을 얻고자하는 방법은 알아보지 않았습니다.

이는 우리가 알고 있는 타입이 아닙니다.

그런데 방법은 있습니다!

왜냐, 우리는 익명 타입을 생성하는 방법에 대해서 지~난 시간에 알아보지 않았습니까?

따라서 알 수 없는 타입의 집합 또한 얻을 수 있습니다.



이처럼 익명 타입 객체들의 집합을 얻을 수 있습니다.

기존에는 만들고자하는 타입을 알 수 있었기 때문에 

new  연산자가 필요가 없었습니다.

그런데, 익명 타입인 내가 모르는 타입의 경우

자동적으로 컴파일러가 만들어주기 위해서는 new 연산자를 통해 익명 타입을 만들어줘야합니다.


기본적으로 익명 타입을 만들게 되면

익명 클래스의 이름이 복잡하게 만들어지고, 인자와 똑같은 속성이 만들어집니다.


아무튼...

이렇게 익명 타입을 만들게 될 경우, var 타입을 통해 추출된 결과물을 받습니다.

그런데 여기서 주의해야할 점이 있습니다.

var 타입은 return 받거나 매개변수로 사용되거나 필드에 사용될 수 없습니다.



따라서 var 타입이기 때문에 return을 var 형식으로 날리게 되면,

LINQ 식은 잘 되었어도 에러가 날 수 있다 이겁니다.


따라서 함수를 호출했는데 

해당 집합에서 내가 원하는 집합만을 추출하여 return 하고 싶은 경우에는 어떻게 해야할까요?


이럴 때에는 익명 타입이기에 var 타입만을 사용할 수 있는 집합을

확장 메서드인 ToList(), ToArray() 등을 통해 변환 후 return 시키면 됩니다.


이 외에도 Enumerable 클래스를 통해

개수를 구하거나 (Count()), 결과물을 역순으로 바꾸거나 (Reverse()),

우리가 기존에 아는 orderby 연산자를 사용해 정렬도 할 수 있습니다.



재밌게도 벤다이어그램 도구를 사용할 수 도 있습니다.

이 벤 다이어그램은 집합에 사용하는 연산이지요...


여기에는 간단하게 Except() 와 같은 차집합,

Intersect() 와 같은 교집합, Union() 와 같은 합집합,

마지막으로 Concat() 과 같은 덧붙이기 집합인 결합집합 기능을 제공해줍니다.


덧붙이기 집합은 합집합과 달리 중복되지요...

그래서 중복을 제거할 수 있는 Distinct() 확장 메서드도 제공해줍니다.


또한, LINQ에서는 집계 연산자도 제공해줍니다.

개수를 세던 Count() 확장 메서드도 해당 연산에 포함이 되지요.


필자가 소개한 기능들은 지금껏 모두 데이터를 가져오는데만 몰두했었습니다.

좀 더 자세하게 말하자면, 일부를 변형 하거나 전체를 가져오는데 썼었지요.


데이터가 엄청 많다고 생각해봅시다.

집계 연산을 제공해준다는 소리는 지금 껏 사용한 기능 외에도

평균, 최대값, 최소값, 합계 등을 구할 수도 있다는 사실을 알려주는 것입니다.

이를 전문 용어로 '스칼라' 라고 부릅니다.

스칼라 연산 이라는 것입니다.



간략하게 코드에 대해서 설명드리자면,

쿼리는 기본적으로 '지연 실행'을 한다고 했습니다.

그런데, .(점)을 통해 어떤 기능을 실행한다는 소리는 '즉시 실행'을 말하는 것 아니겠습니까?

따라서 해당 예제는 LINQ를 '네 번' 때리게 되는 것입니다.


해당 연산에서는 수치가 나오기 시작합니다.

일반적으로 수치가 나온다고 하면

최대값인 Max(), 최소값인 Min(), 평균값인 Average(), 총 합인 Sum()과 같은 

멤버들을 가리키고 이들을 집계 연산이라고 합니다.

집합으로 값을 얻어오는 것이 아닌 수치로 하나를 얻어온다는 소리입니다.

아무튼... LINQ에서는 이런 집계 연산도 제공해주는 사실을 기억하시길 바랍니다.


집합을 대상으로해서 순서를 알 수 없기때문에

가장 큰 녀석의 위치를 알고 싶다면

즉, 값을 얻을 수 는 있찌만, 위치는 얻지 못한다는 소리입니다.


예를 들어 가장 큰 녀석과 가장 작은 녀석을 찾아서 위치를 바꾸고싶다면?

위치라는 것은 순서를 의미하는 것입니다.

그런데 LINQ에서 순서를 바꿔주는 기능을 제공해주지 않기때문에

우리가 직접 구현하는 수 밖에 없습니다.


이번 시간은 여기까지 하도록 하겠습니다.


이상 삽잡이였습니다!