쿼리 구문과 메서드 구문 비교(LINQ)
업데이트: 2007년 11월
LINQ 소개 설명서에서 대부분의 쿼리는 C# 3.0의 선언적 쿼리 구문을 사용하여 쿼리 식으로 작성됩니다. 그러나 .NET CLR(공용 언어 런타임) 자체에는 쿼리 구문의 개념이 없습니다. 따라서 컴파일 타임에 쿼리 식은 CLR이 인식하지 않는 메서드 호출로 변환됩니다. 이러한 메서드를 표준 쿼리 연산자라고 하며 Where, Select, GroupBy, Join, Max, Average 등과 같은 이름을 갖습니다. 쿼리 구문 대신 메서드 구문을 사용하여 이를 직접 호출할 수 있습니다.
일반적으로 더 간단하고 읽기 쉽다는 점에서 쿼리 구문을 사용하는 것이 좋지만 메서드 구문과 쿼리 구문 간에 의미 차이는 없습니다. 또한 지정된 조건과 일치하는 요소 수를 검색하거나 소스 시퀀스에서 최대 값을 가진 요소를 검색하는 쿼리와 같은 일부 쿼리는 메서드 호출로만 표현될 수 있습니다. System.Linq 네임스페이스의 표준 쿼리 연산자에 대한 참조 설명서에서는 일반적으로 메서드 구문이 사용됩니다. 따라서 LINQ 쿼리를 작성하기 시작한 경우에도 쿼리 및 쿼리 식 자체에서 메서드 구문을 사용하는 방법에 익숙하면 도움이 됩니다.
표준 쿼리 연산자 확장 메서드
다음 예제에서는 간단한 쿼리 식 및 메서드 기반 쿼리로 작성된 의미가 같은 쿼리를 보여 줍니다.
class QueryVMethodSyntax
{
static void Main()
{
int[] numbers = { 5, 10, 8, 3, 6, 12};
//Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
foreach (int i in numQuery1)
{
Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
Console.Write(i + " ");
}
// Keep the console open in debug mode.
Console.WriteLine(System.Environment.NewLine);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
/*
Output:
6 8 10 12
6 8 10 12
*/
두 예제의 출력은 동일합니다. 쿼리 변수의 형식이 두 경우 모두에서 IEnumerable<T>로 동일하다는 것을 알 수 있습니다.
메서드 기반 쿼리를 이해하기 위해 좀더 자세히 살펴보도록 하겠습니다. 식의 오른쪽에서 where 절은 이제 numbers 개체에 대한 인스턴스 메서드로 표현되며 해당 형식은 IEnumerable<int>입니다. 제네릭 IEnumerable<T> 인터페이스에 익숙한 경우 Where 메서드가 없다는 것을 알고 있을 것입니다. 그러나 Visual Studio IDE에서 IntelliSense 완성 목록을 호출할 경우 Where 메서드뿐만 아니라 Select, SelectMany, Join 및 Orderby와 같은 다른 많은 메서드를 보게 됩니다. 이러한 메서드는 모두 표준 쿼리 연산자입니다.
IEnumerable<T>이 이러한 추가 메서드를 포함하도록 다시 정의된 것처럼 보이지만 실제로는 그렇지 않습니다. 표준 쿼리 연산자는 확장 메서드라는 새로운 종류의 메서드로 구현됩니다. 확장 메서드는 기존 "형식"을 확장하므로 해당 형식에 대한 인스턴스 메서드인 것처럼 호출될 수 있습니다. 표준 쿼리 연산자가 IEnumerable<T>을 확장하기 때문에 사용자는 numbers.Where(...)를 작성할 수 있습니다.
LINQ를 사용하려면 올바른 using 지시문을 사용하여 확장 메서드를 응용 프로그램의 범위에 가져오는 방법만 알면 됩니다. 이 방법은 방법: LINQ 프로젝트 만들기에 자세히 설명되어 있습니다. 응용 프로그램의 관점에서 보면 확장 메서드와 일반 인스턴스 메서드는 동일합니다.
확장 메서드에 대한 자세한 내용은 확장 메서드(C# 프로그래밍 가이드)를 참조하십시오. 표준 쿼리 연산자에 대한 자세한 내용은 LINQ 일반 프로그래밍 가이드 및 표준 쿼리 연산자 개요를 참조하십시오. LINQ to SQL 및 LINQ to XML과 같은 일부 LINQ 공급자는 IEnumerable<T> 외에 다른 형식을 위한 고유한 표준 쿼리 연산자와 추가 확장 메서드를 구현합니다.
람다 식
앞의 예제에서 조건식(num % 2 == 0)은 인라인 인수로 Where 메서드에 전달됩니다(Where(num => num % 2 == 0). ). 이 인라인 식을 람다 식이라고 합니다. 이 인라인 식은 코드를 작성하는 편리한 방법이며 이 방법이 없을 경우 익명 메서드나 제네릭 대리자 또는 식 트리와 같은 더 번거로운 형식으로 코드를 작성해야 합니다. C#에서 =>는 "goes to"를 나타내는 람다 연산자입니다. 이 연산자 왼쪽의 num은 쿼리 식의 num에 해당하는 입력 변수입니다. 컴파일러에서는 numbers가 제네릭 IEnumerable<T> 형식이라는 것을 알고 있으므로 num의 형식을 유추할 수 있습니다. 람다의 본문은 쿼리 구문이나 다른 모든 C# 식 또는 문의 식과 동일하므로 메서드 호출과 기타 복잡한 논리를 포함할 수 있습니다. "반환 값"은 식 결과입니다.
LINQ를 사용하기 위해 람다를 광범위하게 사용할 필요는 없습니다. 그러나 특정 쿼리는 메서드 구문으로만 표현할 수 있으며 이러한 쿼리 중 일부에 람다 식이 필요합니다. 람다에 더 익숙해지고 나면 LINQ 도구 상자에서 람다가 강력하고 유연한 도구라는 것을 알 수 있습니다. 자세한 내용은 람다 식(C# 프로그래밍 가이드)을 참조하십시오.
쿼리의 작성 가능성
앞의 코드 예제에서는 Where에 대한 호출에서 도트 연산자를 사용하여 OrderBy 메서드가 호출됩니다. Where가 필터링된 시퀀스를 생성한 다음 Orderby는 해당 시퀀스를 정렬하여 해당 시퀀스에서 작동합니다. 쿼리가 IEnumerable을 반환하므로 사용자는 메서드 호출을 함께 연결하여 메서드 구문에서 쿼리를 작성합니다. 쿼리 구문을 사용하여 쿼리를 작성할 때는 컴파일러가 이 작업을 내부적으로 수행합니다. 또한 쿼리 변수가 쿼리 결과를 저장하지 않으므로 쿼리 변수가 실행된 이후라도 언제든지 쿼리 변수를 수정하거나 새 쿼리의 기초로 사용할 수 있습니다.