LINQ를 지원하는 C# 기능

쿼리 식

쿼리 식은 SQL 또는 XQuery와 유사한 선언적 구문을 사용하여 System.Collections.Generic.IEnumerable<T> 컬렉션을 쿼리합니다. 컴파일 시간에 쿼리 구문은 LINQ 공급자의 표준 쿼리 메서드 구현에 대한 메서드 호출로 변환됩니다. 애플리케이션은 using 지시문으로 적절한 네임스페이스를 지정하여 범위 내에 있는 표준 쿼리 연산자를 제어합니다. 다음 쿼리 식은 문자열의 배열을 사용하고 문자열의 첫 번째 문자에 따라 그룹화하며 그룹의 순서를 지정합니다.

var query = from str in stringArray
            group str by str[0] into stringGroup
            orderby stringGroup.Key
            select stringGroup;

암시적으로 형식화된 변수(var)

다음과 같이 var 한정자를 사용하여 컴파일러에 형식을 유추하고 할당하도록 지시할 수 있습니다.

var number = 5;
var name = "Virginia";
var query = from str in stringArray
            where str[0] == 'm'
            select str;

var(으)로 선언된 변수는 명시적으로 지정하는 형식의 변수와 마찬가지로 강력한 형식입니다. var(을)를 사용하면 무명 형식을 만들 수 있지만 지역 변수에 대해서만 만들 수 있습니다. 자세한 내용은 암시적으로 형식화된 지역 변수를 참조하세요.

개체 및 컬렉션 이니셜라이저

개체 및 컬렉션 이니셜라이저를 사용하면 개체에 대한 생성자를 명시적으로 호출하지 않고 개체를 초기화할 수 있습니다. 일반적으로 이니셜라이저는 소스 데이터를 새 데이터 형식으로 프로젝션할 때 쿼리 식에서 사용됩니다. public NamePhone 속성이 있는 Customer라는 클래스를 가정할 경우 다음 코드에서처럼 개체 이니셜라이저를 사용할 수 있습니다.

var cust = new Customer { Name = "Mike", Phone = "555-1212" };

Customer 클래스를 계속 진행하면서 IncomingOrders(이)라는 데이터 원본이 있으며, 큰 OrderSize(이)가 있는 각 주문에 대해 해당 순서를 기반으로 새 Customer(을)를 만들려고 한다고 가정합니다. LINQ 쿼리는 이 데이터 소스에서 실행하고 개체 초기화를 사용하여 컬렉션을 채울 수 있습니다.

var newLargeOrderCustomers = from o in IncomingOrders
                            where o.OrderSize > 5
                            select new Customer { Name = o.Name, Phone = o.Phone };

데이터 원본에는 OrderSize와(과) 같은 Customer 클래스보다 더 많은 속성이 정의되어 있을 수 있지만 개체 초기화를 사용하면 쿼리에서 반환된 데이터가 원하는 데이터 형식으로 성형됩니다. 클래스와 관련된 데이터를 선택합니다. 결과적으로, 이제 원하는 새 Customer(으)로 채워진 System.Collections.Generic.IEnumerable<T>(이)가 있습니다. 앞의 예제는 LINQ의 메서드 구문으로 작성할 수도 있습니다.

var newLargeOrderCustomers = IncomingOrders.Where(x => x.OrderSize > 5).Select(y => new Customer { Name = y.Name, Phone = y.Phone });

C# 12부터는 컬렉션 식을 사용하여 컬렉션을 초기화할 수 있습니다.

자세한 내용은 다음을 참조하세요.

익명 형식

컴파일러는 무명 형식을 생성합니다. 형식 이름은 컴파일러에서만 사용할 수 있습니다. 무명 형식은 별도의 명명된 형식을 정의하지 않고 쿼리 결과에서 일시적으로 속성 집합을 그룹화하는 편리한 방법을 제공합니다. 무명 형식은 다음과 같이 새로운 식과 개체 이니셜라이저를 사용하여 초기화됩니다.

select new {name = cust.Name, phone = cust.Phone};

C# 7부터 튜플을 사용하여 명명되지 않은 형식을 만들 수 있습니다.

확장명 메서드

확장 메서드는 형식과 연결할 수 있는 정적 메서드이므로 형식의 인스턴스 메서드인 것처럼 호출할 수 있습니다. 이 기능을 사용하면 실제로 수정하지 않고도 기존 형식에 새 메서드를 "추가"할 수 있습니다. 표준 쿼리 연산자는 IEnumerable<T>을 구현하는 모든 형식에 대해 LINQ 쿼리 기능을 제공하는 확장 메서드 집합입니다.

람다 식

람다 식=> 연산자를 사용하여 입력 매개 변수를 함수 본문과 분리하고 컴파일 시간에 대리자 또는 식 트리로 변환할 수 있는 인라인 함수입니다. LINQ 프로그래밍에서는 표준 쿼리 연산자에 대한 메서드를 직접 호출할 때 람다 식이 나타납니다.

데이터로서의 식

쿼리 개체는 구성 가능하므로 메서드에서 쿼리를 반환할 수 있습니다. 쿼리를 나타내는 개체는 결과 컬렉션을 저장하지 않고, 대신 필요할 때 결과를 생성하는 단계를 저장합니다. 메서드에서 쿼리 개체를 반환하는 경우 메서드를 추가로 작성하거나 수정할 수 있다는 이점이 있습니다. 따라서 쿼리를 반환하는 메서드의 반환 값 또는 out 매개 변수도 해당 형식을 가지고 있어야 합니다. 메서드가 쿼리를 구체적인 List<T> 또는 Array 형식으로 구체화하는 경우 쿼리 자체가 아니라 쿼리 결과를 반환합니다. 메서드에서 반환되는 쿼리 변수는 여전히 구성 또는 수정 가능합니다.

다음 예제에서 첫 번째 메서드 QueryMethod1은 쿼리를 반환 값으로 반환하고 두 번째 메서드 QueryMethod2는 쿼리를 out 매개 변수로 반환합니다(예제의 returnQ). 두 경우 모두 쿼리 결과가 아니라 쿼리가 반환됩니다.

IEnumerable<string> QueryMethod1(int[] ints) =>
    from i in ints
    where i > 4
    select i.ToString();

void QueryMethod2(int[] ints, out IEnumerable<string> returnQ) =>
    returnQ =
        from i in ints
        where i < 4
        select i.ToString();

int[] nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var myQuery1 = QueryMethod1(nums);

쿼리 myQuery1은 다음 foreach 루프에서 실행됩니다.

foreach (var s in myQuery1)
{
    Console.WriteLine(s);
}

마우스 포인터를 myQuery1에 놓아 형식을 확인합니다.

myQuery1을 사용하지 않고 QueryMethod1에서 반환된 쿼리를 직접 실행할 수도 있습니다.

foreach (var s in QueryMethod1(nums))
{
    Console.WriteLine(s);
}

마우스 포인터를 QueryMethod1 호출 위에 놓아 반환 형식을 확인합니다.

QueryMethod2는 쿼리를 out 매개 변수의 값으로 반환합니다.

QueryMethod2(nums, out IEnumerable<string> myQuery2);

// Execute the returned query.
foreach (var s in myQuery2)
{
    Console.WriteLine(s);
}

쿼리 컴퍼지션을 사용하여 쿼리를 수정할 수 있습니다. 이 경우 이전 쿼리 개체를 사용하여 새 쿼리 개체를 만듭니다. 이 새 개체는 원래 쿼리 개체와 다른 결과를 반환합니다.

myQuery1 =
    from item in myQuery1
    orderby item descending
    select item;

// Execute the modified query.
Console.WriteLine("\nResults of executing modified myQuery1:");
foreach (var s in myQuery1)
{
    Console.WriteLine(s);
}