LINQ는 쿼리 언어와 그 쿼리 언어를 일련의 메서드 집합으로 변환하는 2개의 핵심 구조를 기반으로

한다. C# 컴파일러는 쿼리 언어로 작성된 쿼리 표현식을 메서드 호출 구문으로 변환해 준다.

모든 쿼리 표현식은 하나 혹은 여러 개의 메서드로 매핑된다. 이러한 매핑 관계는 두 가지 관점으로

분리해서 생각해봐야 한다. 클래스 사용자의 관점에서 볼 때 쿼리 표현식은 단순히 메서드 호출 구문

의 다른 표현 방법일 뿐이다. where 절은 적절한 인자를 이용하여 Where() 라는 메서드를 호출하는

코드로 변환된다. 클래스 설계자의 관점에서는 기본 프레임워크에서 제공하는 메서드들이 어떻게

구현됐는지를 살펴보고 더 나은 방법으로 구현할 수 있을지를 판단해야 한다. 더 나은 구현 방법이

없다면 기본 라이브러리를 그대로 사용하면 되겠지만 개선의 가능성이 있다면 우선 쿼리 표현식이

메서드 호출 구문으로 어떻게 변환되었는지를 완벽하게 이해해야 하며, 이러한 이해를 기반으로

메서드의 원형을 올바르게 정의해서 메서드 호출 방식으로의 변환이 올바르게 수행될 수 있도록

코드를 작성해야 한다. 대체로 구현 방식이 명확하지만 일부 복잡한 표현식의 경우 상당히 까다로운

경우도 있다.

먼저 사용자의 관점에서 접근해보자. 전체 쿼리 표현식 패턴에는 11가지의 매서드가 포함되어 있다.

다음은 엔데스 헤일스버그(Anders Hejlsberg), 매드 토거슨(Mads Torgeersen), 스콧 빌타무스(Scott

Wiltamuth), 피터 골드(Peter Golde)가 2009년 출간한 [C# Programming Language] 3판의 7.15.3절의

내용을 허락받아 발췌한 것이다.

delegate R Func<T1, R>(T1 arg1);
delegate R Func<T1, T2, R>(T1 arg1, T2 arg2);

class C
{
		public C<T> Cast<T>();
}

class C<T> : C
{
		public C<T> Where(Func<T, bool> predicate);
		public C<U> Select<U>(Func<T, U> selector);
		public C<V> SelectMany<U, V>(Func<T, C<U>> selector,
				Func<T, U, V> resultSelector);
		public C<V> Join<U, K, V>(C<U> inner,
				Func<T, K> outerKeySelector,
				Func<U, K> innerKeySelector,
				Func<T, C<U>, V> resultSelector);
		public C<V> GroupJoin<U, K, V>(C<U> inner,
				Func<T, K> outerKeySelector,
				Func<U, K> innerKeySelector,
				Func<T, C<U>, V> resultSelector);
		public O<T> OrderBy<K>(Func<T, K> keySelector);
		public O<T> OrderByDescending<K>(Func<T, K> keySelector);
		public C<G<K, T>> GroupBy<K>(Func<T, K> keySelector);
		public C<G<K, E>> GroupBy<K, E>(Func<T, K> keySelector,
				Func<T, E> elementSelector);
}

class O<T> : C<T>
{
		public O<T> ThenBy<K>(Func<T, K> keySelector);
		public O<T> ThenByDesending<K>(Func<T, K> keySelector);
}

class G<K, T> : C<T>
{
		public K Key { get; }
}