연습: C#에서 쿼리 작성(LINQ)
이 연습에서는 LINQ 쿼리 식을 작성하는 데 사용되는 C# 언어 기능을 보여 줍니다. 이 연습을 마치고 나면 LINQ to SQL, LINQ to DataSet 또는 LINQ to XML과 같은 원하는 특정 LINQ 공급자에 대한 샘플과 설명서를 진행할 수 있습니다.
사전 요구 사항
이 연습을 수행하려면 Visual Studio 2010이 필요합니다.
이 항목의 비디오 버전을 보려면 Video How to: Writing Queries in C# (LINQ)을 참조하십시오.
C# 프로젝트 만들기
.NET Framework 버전 3.5를 대상으로 하는 C# 프로젝트를 만들려면
Visual Studio를 시작합니다.
파일 메뉴에서 새로 만들기를 가리킨 다음 프로젝트를 클릭합니다.
새 프로젝트 대화 상자의 오른쪽 위 모퉁이에는 세 개의 아이콘이 있습니다. 왼쪽 아이콘을 클릭하고 .NET Framework 버전 3.5가 선택되었는지 확인합니다.
Visual Studio에 설치되어 있는 템플릿에서 콘솔 응용 프로그램 아이콘을 클릭합니다.
기본 이름을 사용하거나 새 응용 프로그램 이름을 입력하고 확인을 클릭합니다.
프로젝트에는 System.Core.dll에 대한 참조 및 System.Linq 네임스페이스에 대한 using 지시문이 있습니다.
메모리 내 데이터 소스 만들기
쿼리의 데이터 소스는 간단한 Student 개체 목록입니다. 각 Student 레코드는 이름, 성 및 클래스에서의 테스트 점수를 나타내는 정수 배열을 포함합니다. 프로젝트에 이 코드를 복사합니다. 다음 특성에 주의합니다.
Student 클래스는 자동 구현된 속성으로 구성됩니다.
목록의 각 학생은 개체 이니셜라이저를 사용하여 초기화됩니다.
목록 자체는 컬렉션 이니셜라이저를 사용하여 초기화됩니다.
이 전체 데이터 구조는 명시적 멤버 액세스나 생성자에 대한 명시적 호출 없이 초기화 및 인스턴스화됩니다. 이러한 새로운 기능에 대한 자세한 내용은 자동으로 구현된 속성(C# 프로그래밍 가이드) 및 개체 및 컬렉션 이니셜라이저(C# 프로그래밍 가이드)를 참조하십시오.
데이터 소스를 추가하려면
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} } };
새 학생을 학생 목록에 추가하려면
- 새 Student를 Students 목록에 추가하고 원하는 이름과 테스트 점수를 사용합니다. 개체 이니셜라이저의 구문을 더 잘 배울 수 있도록 새 학생 정보를 모두 직접 입력해 봅니다.
쿼리 만들기
간단한 쿼리를 만들려면
응용 프로그램의 Main 메서드에서 첫 번째 테스트의 점수가 90점을 넘는 모든 학생의 목록을 실행 시에 생성하는 간단한 쿼리를 만듭니다. 전체 Student 개체가 선택되어 있으므로 쿼리의 형식은 IEnumerable<Student>입니다. var 키워드를 사용하여 코드에서 암시적 형식 지정을 사용할 수도 있지만 결과를 더 분명하게 나타내기 위해 명시적 형식 지정이 사용됩니다. var에 대한 자세한 내용은 암시적으로 형식화된 지역 변수(C# 프로그래밍 가이드)을 참조하십시오.
또한 쿼리의 범위 변수인 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;
쿼리 실행
쿼리를 실행하려면
이제 쿼리를 실행하게 만드는 foreach 루프를 작성합니다. 코드에 대한 다음 사항에 주의합니다.
반환된 시퀀스의 각 요소는 foreach 루프에서 반복 변수를 통해 액세스합니다.
이 변수의 형식은 Student이고 쿼리 변수의 형식은 호환되는 IEnumerable<Student>입니다.
이 코드를 추가한 후 Ctrl + F5를 눌러 응용 프로그램을 빌드 및 실행하여 콘솔 창에서 결과를 봅니다.
// Execute the query.
// var could be used here also.
foreach (Student student in studentQuery)
{
Console.WriteLine("{0}, {1}", student.Last, student.First);
}
다른 필터 조건을 추가하려면
쿼리를 더 구체화하기 위해 where 절에서 여러 부울 조건을 결합할 수 있습니다. 다음 코드는 첫 번째 점수가 90점을 넘고 마지막 점수가 80점 미만인 학생을 쿼리에서 반환하도록 조건을 추가합니다. where 절은 다음 코드와 유사해야 합니다.
where student.Scores[0] > 90 && student.Scores[3] < 80
자세한 내용은 where 절(C# 참조)을 참조하십시오.
쿼리 수정
결과의 순서를 지정하려면
결과가 일정한 순서로 되어 있을 경우 결과를 더 쉽게 검색할 수 있습니다. 소스 요소의 액세스 가능한 임의 필드별로 반환된 시퀀스의 순서를 지정할 수 있습니다. 예를 들어 다음 orderby 절은 각 학생의 성에 따라 사전순으로 A부터 Z까지 결과의 순서를 지정합니다. 다음 orderby 절을 쿼리에서 where 문 바로 뒤 및 select 문 앞에 추가합니다.
orderby student.Last ascending
이제 첫 번째 테스트의 점수에 따라 최고 점수부터 최하 점수까지 역순으로 결과의 순서를 지정하도록 orderby 절을 변경합니다.
orderby student.Scores[0] descending
점수를 볼 수 있도록 WriteLine 형식 문자열을 변경합니다.
Console.WriteLine("{0}, {1} {2}", student.Last, student.First, student.Scores[0]);
자세한 내용은 orderby 절(C# 참조)을 참조하십시오.
결과를 그룹화하려면
그룹화는 쿼리 식의 강력한 기능입니다. group 절이 있는 쿼리는 그룹 시퀀스를 생성하고 각 그룹 자체는 Key와 해당 그룹의 모든 멤버로 구성된 시퀀스를 포함합니다. 다음 새 쿼리는 성의 첫 글자를 키로 사용하여 학생을 그룹화합니다.
// studentQuery2 is an IEnumerable<IGrouping<char, Student>> var studentQuery2 = from student in students group student by student.Last[0];
이제 쿼리 형식이 변경되었다는 것에 주의합니다. char 형식을 키로 포함하고 시퀀스와 Student 개체 시퀀스를 포함하는 그룹 시퀀스가 생성됩니다. 쿼리 형식이 변경되었으므로 다음 코드에서는 또한 foreach 실행 루프가 변경됩니다.
// studentGroup is a IGrouping<char, Student> foreach (var studentGroup in studentQuery2) { Console.WriteLine(studentGroup.Key); foreach (Student student in studentGroup) { Console.WriteLine(" {0}, {1}", student.Last, student.First); } }
Ctrl + F5를 눌러 응용 프로그램을 실행하고 콘솔 창에서 결과를 봅니다.
자세한 내용은 group 절(C# 참조)을 참조하십시오.
변수의 형식이 암시적으로 지정되게 하려면
IGroupings의 IEnumerables에 대한 코드를 명시적으로 작성하는 작업은 금방 지루해질 수 있습니다. var를 사용하면 동일한 쿼리와 foreach 루프를 훨씬 더 편리하게 작성할 수 있습니다. var 키워드는 개체의 형식을 변경하지 않으며 단순히 형식을 유추할 것을 컴파일러에 지시합니다. 반복 변수 group 및 studentQuery의 형식을 var로 변경하고 쿼리를 다시 실행합니다. 이 경우 내부 foreach 루프에서 반복 변수의 형식은 여전히 Student로 지정되며 쿼리가 이전과 동일하게 작동합니다. s 반복 변수를 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); } }
var에 대한 자세한 내용은 암시적으로 형식화된 지역 변수(C# 프로그래밍 가이드)을 참조하십시오.
키 값별로 그룹의 순서를 지정하려면
앞의 쿼리를 실행하면 그룹은 사전순으로 되어 있지 않습니다. 그룹에 이 순서를 지정하려면 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); } }
이 쿼리를 실행하면 그룹은 이제 사전순으로 정렬됩니다.
let을 사용하여 식별자를 제공하려면
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); }
자세한 내용은 let 절(C# 참조)을 참조하십시오.
쿼리 식에서 메서드 구문을 사용하려면
LINQ 쿼리 구문과 메서드 구문 비교(C#)에 설명된 것처럼 일부 쿼리 작업은 메서드 구문에 의해서만 표현될 수 있습니다. 다음 코드는 소스 시퀀스에서 각 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);
Select 절에서 변환 또는 프로젝션하려면
쿼리에서는 소스 시퀀스의 요소와 다른 요소를 가진 시퀀스를 생성하는 것이 매우 일반적입니다. 이전 쿼리 또는 실행 루프를 삭제하거나 주석으로 처리하고 다음 코드로 바꿉니다. 쿼리는 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); }
이 연습의 앞부분에 있는 코드는 평균 클래스 점수가 약 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); }
다음 단계
C#에서 쿼리를 사용하는 기본적인 측면에 익숙해진 후에는 원하는 특정 LINQ 공급자 형식에 대한 설명서와 샘플을 읽을 수 있습니다.