연습: C#에서 쿼리 작성(LINQ)

이 연습에서는 LINQ 쿼리 식을 작성하는 데 사용되는 C # 언어 기능을 보여 줍니다.

C# 프로젝트 만들기

참고

다음 지침은 Visual Studio용입니다. 다른 개발 환경을 사용하는 경우 System.Core.dll에 대한 참조와 System.Linq 네임스페이스에 대한 using 지시문을 사용하여 콘솔 프로젝트를 만듭니다.

Visual Studio에서 프로젝트를 만들려면

  1. Visual Studio를 시작합니다.

  2. 메뉴 모음에서 파일, 새로 만들기, 프로젝트를 차례로 선택합니다.

    새 프로젝트 대화 상자가 열립니다.

  3. 설치됨, 템플릿, Visual C# 을 차례로 확장하고 콘솔 애플리케이션을 선택합니다.

  4. 이름 텍스트 상자에 다른 이름을 입력하거나 기본 이름을 선택한 다음 확인 단추를 선택합니다.

    솔루션 탐색기에 새 프로젝트가 표시됩니다.

  5. 프로젝트에 System.Core.dll에 대한 참조 및 System.Linq 네임스페이스에 대한 using 지시문이 있습니다.

메모리 내 데이터 소스 만들기

쿼리의 데이터 소스는 간단한 Student 개체 목록입니다. 각 Student 레코드에는 이름, 성 및 클래스의 테스트 점수를 나타내는 정수 배열이 있습니다. 프로젝트에 이 코드를 복사합니다. 다음 특성에 주의합니다.

  • Student 클래스는 자동으로 구현된 속성으로 구성됩니다.

  • 목록의 각 학생은 개체 이니셜라이저로 초기화됩니다.

  • 목록 자체는 컬렉션 이니셜라이저로 초기화됩니다.

이 전체 데이터 구조는 생성자 또는 명시적 멤버 액세스에 대한 명시적 호출 없이 초기화되고 인스턴스화됩니다. 이러한 새로운 기능에 대한 자세한 내용은 자동으로 구현된 속성개체 및 컬렉션 이니셜라이저를 참조하세요.

데이터 소스를 추가하려면

  • Student 클래스 및 초기화된 학생 목록을 프로젝트의 Program 클래스에 추가합니다.

    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public List<int> Scores;
    }
    
    // Create a data source by using a collection initializer.
    static List<Student> students = new List<Student>
    {
        new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}},
        new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
        new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}},
        new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}},
        new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}},
        new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}},
        new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}},
        new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}},
        new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}},
        new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}},
        new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}},
        new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91}}
    };
    

학생 목록에 새 학생을 추가하려면

  1. StudentStudents 목록에 추가하고 원하는 이름 및 시험 점수를 사용합니다. 개체 이니셜라이저의 구문을 더 잘 알 수 있도록 새로운 학생 정보를 모두 입력해 보세요.

쿼리 만들기

단순 쿼리를 작성하려면

  • 애플리케이션의 Main 메서드에서, 실행 시 첫 번째 테스트의 점수가 90보다 큰 모든 학생의 목록을 생성하는 간단한 쿼리를 만듭니다. 전체 Student 개체가 선택되므로 쿼리의 형식은 IEnumerable<Student>입니다. var 키워드를 사용하여 코드에서 암시적 형식을 사용할 수도 있지만, 결과를 명확하게 설명하기 위해 명시적 형식이 사용됩니다. var에 대한 자세한 내용은 암시적으로 형식화된 지역 변수를 참조하세요.

    또한 쿼리의 범위 변수 student는 소스의 각 Student에 대한 참조로 사용되며, 각 개체에 대한 멤버 액세스를 제공합니다.

// Create the query.
// The first line could also be written as "var studentQuery ="
IEnumerable<Student> studentQuery =
    from student in students
    where student.Scores[0] > 90
    select student;

쿼리 실행

쿼리를 실행하려면

  1. 이제 쿼리를 실행하도록 할 foreach 루프를 작성합니다. 다음은 코드에 대한 유의 사항입니다.

    • 반환된 시퀀스의 각 요소는 foreach 루프의 반복 변수를 통해 액세스됩니다.

    • 이 변수의 형식은 Student이며, 쿼리 변수 형식은 IEnumerable<Student>과 호환됩니다.

  2. 이 코드를 추가한 후 애플리케이션을 빌드하고 실행하고 콘솔 창에서 결과를 확인하세요.

// Execute the query.
// var could be used here also.
foreach (Student student in studentQuery)
{
    Console.WriteLine("{0}, {1}", student.Last, student.First);
}

// Output:
// Omelchenko, Svetlana
// Garcia, Cesar
// Fakhouri, Fadi
// Feng, Hanying
// Garcia, Hugo
// Adams, Terry
// Zabokritski, Eugene
// Tucker, Michael

다른 필터 조건을 추가하려면

  1. 쿼리를 구체화하기 위해 where 절에서 여러 부울 조건을 결합할 수 있습니다. 다음 코드는 쿼리를 실행하여 첫 번째 점수가 90을 초과하고 마지막 점수가 80 미만인 학생들을 반환하도록 하는 조건을 추가합니다. where 절은 다음 코드와 유사합니다.

    where student.Scores[0] > 90 && student.Scores[3] < 80  
    

    자세한 내용은 where 절을 참조하세요.

쿼리 수정

결과를 정렬하려면

  1. 특정 순서로 되어 있는 경우 결과를 더 쉽게 검색할 수 있습니다. 반환된 시퀀스를 소스 요소에서 액세스 가능한 필드 기준으로 정렬할 수 있습니다. 예를 들어, 다음 orderby 절은 각 학생의 성에 따라 결과를 사전순으로 정렬합니다. where 문 바로 다음과 select 문 앞에서 다음 orderby 절을 쿼리에 추가합니다.

    orderby student.Last ascending  
    
  2. 이제 가장 높은 점수에서 가장 낮은 점수까지 첫 번째 테스트의 점수에 따라 역순으로 결과를 정렬하도록 orderby 절을 변경합니다.

    orderby student.Scores[0] descending  
    
  3. 점수를 볼 수 있도록 WriteLine 형식 문자열을 변경합니다.

    Console.WriteLine("{0}, {1} {2}", student.Last, student.First, student.Scores[0]);  
    

    자세한 내용은 orderby 절을 참조하세요.

결과를 그룹화하려면

  1. 그룹화는 쿼리 식의 강력한 기능입니다. 그룹 절이 있는 쿼리는 그룹 시퀀스를 생성하며, 각 그룹 자체는 Key 및 해당 그룹의 모든 멤버로 구성된 시퀀스를 포함합니다. 다음과 같은 새 쿼리는 학생 성의 첫 글자를 키로 사용하여 학생들을 그룹화합니다.

    IEnumerable<IGrouping<char, Student>> studentQuery2 =
        from student in students
        group student by student.Last[0];
    
  2. 이제 쿼리 형식이 변경되었습니다. 이제 쿼리를 실행하면 char 형식을 키로 가지고 있고 Student 개체의 시퀀스를 가지고 있는 그룹의 시퀀스가 생성됩니다. 쿼리 형식이 변경되었으므로 다음 코드는 foreach 실행 루프도 변경합니다.

    foreach (IGrouping<char, Student> studentGroup in studentQuery2)
    {
        Console.WriteLine(studentGroup.Key);
        foreach (Student student in studentGroup)
        {
            Console.WriteLine("   {0}, {1}",
                      student.Last, student.First);
        }
    }
    
    // Output:
    // O
    //   Omelchenko, Svetlana
    //   O'Donnell, Claire
    // M
    //   Mortensen, Sven
    // G
    //   Garcia, Cesar
    //   Garcia, Debra
    //   Garcia, Hugo
    // F
    //   Fakhouri, Fadi
    //   Feng, Hanying
    // T
    //   Tucker, Lance
    //   Tucker, Michael
    // A
    //   Adams, Terry
    // Z
    //   Zabokritski, Eugene
    
  3. 애플리케이션을 실행하고 콘솔 창에서 결과를 봅니다.

    자세한 내용은 group 절을 참조하세요.

변수를 암시적으로 형식화하려면

  1. IGroupingsIEnumerables를 명시적으로 코딩하는 작업은 지루할 수 있습니다. var을 사용하여 동일한 쿼리 및 foreach 루프를 훨씬 더 편리하게 작성할 수 있습니다. var 키워드는 개체 형식을 변경하지 않고, 형식을 추론하도록 컴파일러에 지시합니다. studentQuery의 형식 및 group 반복 변수를 var로 변경하고 쿼리를 다시 실행합니다. 내부 foreach 루프에서 반복 변수의 형식은 여전히 Student로 지정되며 쿼리는 이전과 마찬가지로 작동합니다. student 반복 변수를 var로 변경하고 쿼리를 다시 실행합니다. 정확히 동일한 결과가 표시됩니다.

    var studentQuery3 =
        from student in students
        group student by student.Last[0];
    
    foreach (var groupOfStudents in studentQuery3)
    {
        Console.WriteLine(groupOfStudents.Key);
        foreach (var student in groupOfStudents)
        {
            Console.WriteLine("   {0}, {1}",
                student.Last, student.First);
        }
    }
    
    // Output:
    // O
    //   Omelchenko, Svetlana
    //   O'Donnell, Claire
    // M
    //   Mortensen, Sven
    // G
    //   Garcia, Cesar
    //   Garcia, Debra
    //   Garcia, Hugo
    // F
    //   Fakhouri, Fadi
    //   Feng, Hanying
    // T
    //   Tucker, Lance
    //   Tucker, Michael
    // A
    //   Adams, Terry
    // Z
    //   Zabokritski, Eugene
    

    var에 대한 자세한 내용은 암시적으로 형식화된 지역 변수를 참조하세요.

키 값을 기준으로 그룹을 정렬하려면

  1. 이전 쿼리를 실행하면 그룹이 사전순으로 표시되지 않습니다. 이를 변경하려면 group 절 뒤에 orderby 절을 제공해야 합니다. 그러나 orderby 절을 사용하려면 우선 group 절로 만든 그룹에 대한 참조 역할을 하는 식별자가 필요합니다. 다음과 같이 into 키워드를 사용하여 식별자를 제공합니다.

    var studentQuery4 =
        from student in students
        group student by student.Last[0] into studentGroup
        orderby studentGroup.Key
        select studentGroup;
    
    foreach (var groupOfStudents in studentQuery4)
    {
        Console.WriteLine(groupOfStudents.Key);
        foreach (var student in groupOfStudents)
        {
            Console.WriteLine("   {0}, {1}",
                student.Last, student.First);
        }
    }
    
    // Output:
    //A
    //   Adams, Terry
    //F
    //   Fakhouri, Fadi
    //   Feng, Hanying
    //G
    //   Garcia, Cesar
    //   Garcia, Debra
    //   Garcia, Hugo
    //M
    //   Mortensen, Sven
    //O
    //   Omelchenko, Svetlana
    //   O'Donnell, Claire
    //T
    //   Tucker, Lance
    //   Tucker, Michael
    //Z
    //   Zabokritski, Eugene
    

    이 쿼리를 실행하면 이제 그룹이 사전순으로 정렬되는 것을 알 수 있습니다.

let을 사용하여 식별자를 소개하려면

  1. let 키워드를 사용하여 쿼리 식의 식 결과에 대한 식별자를 소개할 수 있습니다. 이 식별자는 다음 예제에서처럼 편리하게 사용할 수도 있고, 여러 번 계산할 필요가 없도록 표현식의 결과를 저장하여 성능을 향상시킬 수도 있습니다.

    // studentQuery5 is an IEnumerable<string>
    // This query returns those students whose
    // first test score was higher than their
    // average score.
    var studentQuery5 =
        from student in students
        let totalScore = student.Scores[0] + student.Scores[1] +
            student.Scores[2] + student.Scores[3]
        where totalScore / 4 < student.Scores[0]
        select student.Last + " " + student.First;
    
    foreach (string s in studentQuery5)
    {
        Console.WriteLine(s);
    }
    
    // Output:
    // Omelchenko Svetlana
    // O'Donnell Claire
    // Mortensen Sven
    // Garcia Cesar
    // Fakhouri Fadi
    // Feng Hanying
    // Garcia Hugo
    // Adams Terry
    // Zabokritski Eugene
    // Tucker Michael
    

    자세한 내용은 let 절을 참조하세요.

쿼리 식에서 메서드 구문을 사용하려면

  1. LINQ의 쿼리 구문 및 메서드 구문에 설명된 대로 일부 쿼리 작업은 메서드 구문을 사용해야만 표현할 수 있습니다. 다음 코드는 소스 시퀀스의 각 Student에 대한 총 점수를 계산한 다음, 해당 쿼리의 결과에 대해 Average() 메서드를 호출하여 클래스의 평균 점수를 계산합니다.

    var studentQuery6 =
        from student in students
        let totalScore = student.Scores[0] + student.Scores[1] +
            student.Scores[2] + student.Scores[3]
        select totalScore;
    
    double averageScore = studentQuery6.Average();
    Console.WriteLine("Class average score = {0}", averageScore);
    
    // Output:
    // Class average score = 334.166666666667
    

select 절에서 변환 또는 프로젝션하려면

  1. 쿼리가 소스 시퀀스의 요소와 다른 요소를 갖는 시퀀스를 생성하는 것은 매우 일반적입니다. 이전 쿼리 및 실행 루프를 삭제하거나 주석으로 처리하고 다음 코드로 바꿉니다. 쿼리는 문자열 시퀀스(Students 아님)를 반환하며 이 사실은 foreach 루프에 반영됩니다.

    IEnumerable<string> studentQuery7 =
        from student in students
        where student.Last == "Garcia"
        select student.First;
    
    Console.WriteLine("The Garcias in the class are:");
    foreach (string s in studentQuery7)
    {
        Console.WriteLine(s);
    }
    
    // Output:
    // The Garcias in the class are:
    // Cesar
    // Debra
    // Hugo
    
  2. 이 연습의 앞부분에 있는 코드는 평균 클래스 점수가 약 334임을 나타냅니다. 총점이 클래스 평균보다 큰 Students의 시퀀스를 Student ID와 함께 생성하려면 select 문에서 무명 형식을 사용할 수 있습니다.

    var studentQuery8 =
        from student in students
        let x = student.Scores[0] + student.Scores[1] +
            student.Scores[2] + student.Scores[3]
        where x > averageScore
        select new { id = student.ID, score = x };
    
    foreach (var item in studentQuery8)
    {
        Console.WriteLine("Student ID: {0}, Score: {1}", item.id, item.score);
    }
    
    // Output:
    // Student ID: 113, Score: 338
    // Student ID: 114, Score: 353
    // Student ID: 116, Score: 369
    // Student ID: 117, Score: 352
    // Student ID: 118, Score: 343
    // Student ID: 120, Score: 341
    // Student ID: 122, Score: 368
    

다음 단계

C#에서 쿼리 작업의 기본 사항에 익숙해지면 관심이 있는 특정 LINQ 공급자 형식에 대한 설명서와 샘플을 읽을 준비가 된 것입니다.

LINQ to SQL

LINQ to DataSet

LINQ to XML(C#)

LINQ to Objects(C#)

참조