Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом руководстве вы создадите источник данных и напишите несколько запросов LINQ. Вы можете поэкспериментировать с выражениями запроса и увидеть различия в результатах. В этом пошаговом руководстве показаны функции языка C#, используемые для записи выражений запросов LINQ. Вы можете следить за процессом, создавать приложение и самостоятельно экспериментировать с запросами. В этой статье предполагается, что вы установили последний пакет SDK для .NET. В противном случае перейдите на страницу загрузки .NET и установите последнюю версию на компьютере.
Сначала создайте приложение. В консоли введите следующую команду:
dotnet new console -o WalkthroughWritingLinqQueries
Или, если вы предпочитаете Visual Studio, создайте консольное приложение с именем WalkthroughWritingLinqQueries.
Создание источника данных в памяти
Первым шагом является создание источника данных для запросов. Источник данных для запросов — это простой список Student
записей. Каждая Student
запись имеет имя, фамилию и массив целых чисел, который представляет их оценки тестов в классе. Добавьте новый файл с именем students.cs и скопируйте следующий код в этот файл:
namespace WalkthroughWritingLinqQueries;
public record Student(string First, string Last, int ID, int[] Scores);
Обратите внимание на следующие характеристики:
- Запись
Student
состоит из автоматически реализованных свойств. - Каждый студент в списке инициализируется основным конструктором.
- Последовательность оценок для каждого студента инициализируется основным конструктором.
Затем создайте последовательность записей Student
, которые служат источником этого запроса. Откройте Program.cs и удалите следующий стандартный код:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
Замените его следующим кодом, который создает последовательность записей Student
:
using WalkthroughWritingLinqQueries;
// Create a data source by using a collection initializer.
IEnumerable<Student> students =
[
new Student(First: "Svetlana", Last: "Omelchenko", ID: 111, Scores: [97, 92, 81, 60]),
new Student(First: "Claire", Last: "O'Donnell", ID: 112, Scores: [75, 84, 91, 39]),
new Student(First: "Sven", Last: "Mortensen", ID: 113, Scores: [88, 94, 65, 91]),
new Student(First: "Cesar", Last: "Garcia", ID: 114, Scores: [97, 89, 85, 82]),
new Student(First: "Debra", Last: "Garcia", ID: 115, Scores: [35, 72, 91, 70]),
new Student(First: "Fadi", Last: "Fakhouri", ID: 116, Scores: [99, 86, 90, 94]),
new Student(First: "Hanying", Last: "Feng", ID: 117, Scores: [93, 92, 80, 87]),
new Student(First: "Hugo", Last: "Garcia", ID: 118, Scores: [92, 90, 83, 78]),
new Student("Lance", "Tucker", 119, [68, 79, 88, 92]),
new Student("Terry", "Adams", 120, [99, 82, 81, 79]),
new Student("Eugene", "Zabokritski", 121, [96, 85, 91, 60]),
new Student("Michael", "Tucker", 122, [94, 92, 91, 91])
];
- Последовательность студентов инициализируется коллекционным выражением.
- Тип
Student
записи содержит статический список всех учащихся. - Некоторые вызовы конструктора используют именованные аргументы для уточнения того, какой аргумент соответствует параметру конструктора.
Попробуйте добавить в список студентов еще несколько учащихся с различными оценками тестов, чтобы лучше разобраться в коде.
Создание запроса
Затем создайте первый запрос. При выполнении запроса создается список всех учащихся, оценка которых на первом тесте превышает 90. Так как выбран весь Student
объект, тип запроса .IEnumerable<Student>
Хотя код также может использовать неявную типизацию с помощью ключевого слова var, явная типизация используется для четкой иллюстрации результатов. (Дополнительные сведения см. в var
разделе "Неявно типизированные локальные переменные".) Добавьте следующий код в Program.cs после кода, создающего последовательность учащихся:
// 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;
Переменная диапазона запроса student
служит ссылкой на каждый Student
в источнике, предоставляя доступ к членам каждого объекта.
Выполнение запроса
Теперь напишите foreach
цикл, который приводит к выполнению запроса. Доступ к каждому элементу в возвращаемой последовательности осуществляется через переменную итерации в цикле foreach
. Тип этой переменной — Student
, и тип переменной запроса совместим — IEnumerable<Student>
. После добавления следующего кода выполните сборку и запустите приложение, чтобы просмотреть результаты в окне консоли .
// Execute the query.
// var could be used here also.
foreach (Student student in studentQuery)
{
Console.WriteLine($"{student.Last}, {student.First}");
}
// Output:
// Omelchenko, Svetlana
// Garcia, Cesar
// Fakhouri, Fadi
// Feng, Hanying
// Garcia, Hugo
// Adams, Terry
// Zabokritski, Eugene
// Tucker, Michael
Чтобы дополнительно уточнить запрос, можно объединить несколько логических условий в предложении where
. Следующий код добавляет условие, чтобы запрос возвращал тех учащихся, чьи первые оценки были более 90 и чьи последние оценки были менее 80. Условие where
должно соответствовать следующему коду.
where student.Scores[0] > 90 && student.Scores[3] < 80
Попробуйте предыдущее where
предложение или поэкспериментируйте с другими условиями фильтра. Для получения дополнительной информации см. раздел , где упоминается условие.
Упорядочить результаты запроса
Проще проверить результаты, если они находятся в каком-то порядке. Вы можете упорядочить возвращаемую последовательность любым доступным полем в исходных элементах. Например, следующее orderby
предложение упорядочивает результаты в алфавитном порядке от A до Z в соответствии с именем семьи каждого учащегося. Добавьте следующее orderby
предложение в запрос сразу после инструкции where
и перед инструкцией select
:
orderby student.Last ascending
Теперь измените orderby
предложение таким образом, чтобы оно упорядочивало результаты в обратном порядке по оценке на первом тесте, от самой высокой оценки к самой низкой.
orderby student.Scores[0] descending
Измените WriteLine
строку формата, чтобы увидеть оценки:
Console.WriteLine($"{student.Last}, {student.First} {student.Scores[0]}");
Дополнительные сведения см. в предложении orderby .
Группирование результатов
Группирование — это мощная возможность в выражениях запросов. Запрос с оператором группировки создает последовательность групп, и каждая группа содержит Key
и последовательность, состоящую из всех членов этой группы. В следующем новом запросе учащиеся используют первую букву имени семьи в качестве ключа.
IEnumerable<IGrouping<char, Student>> studentQuery =
from student in students
group student by student.Last[0];
Тип запроса изменился. Теперь он создает последовательность групп с типом char
в качестве ключа и последовательностью Student
объектов. Код в цикле foreach
выполнения также должен измениться:
foreach (IGrouping<char, Student> studentGroup in studentQuery)
{
Console.WriteLine(studentGroup.Key);
foreach (Student student in studentGroup)
{
Console.WriteLine($" {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
Запустите приложение и просмотрите результаты в окне консоли . Дополнительные сведения см. в группы пункте.
Явное кодирование IEnumerables
IGroupings
может быстро стать утомительным. Создание одного и того же запроса и foreach
цикла гораздо удобнее с помощью var
. Ключевое слово var
не изменяет типы ваших объектов; оно просто указывает компилятору выводить типы. Измените тип studentQuery
и тип переменной итерации group
на var
, затем повторно выполните запрос. В внутреннем foreach
цикле переменная итерации по-прежнему вводится как Student
, и запрос работает как раньше. Измените переменную итерации student
на var
и снова запустите запрос. Вы видите, что вы получаете точно те же результаты.
IEnumerable<IGrouping<char, Student>> studentQuery =
from student in students
group student by student.Last[0];
foreach (IGrouping<char, Student> studentGroup in studentQuery)
{
Console.WriteLine(studentGroup.Key);
foreach (Student student in studentGroup)
{
Console.WriteLine($" {student.Last}, {student.First}");
}
}
Дополнительные сведения см. в var
разделе "Неявно типизированные локальные переменные".
Упорядочить группы по значению ключа
Группы в предыдущем запросе не в алфавитном порядке. После 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($" {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
можно использовать для введения идентификатора для результата любого выражения в запросе. Этот идентификатор может быть удобным, как показано в следующем примере. Кроме того, он может повысить производительность, сохраняя результаты выражения, чтобы его не нужно вычислять несколько раз.
// 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
.
Использование синтаксиса метода в выражении запроса
Как описано в синтаксисе запросов и синтаксисе метода в LINQ, некоторые операции запроса могут быть выражены только с помощью синтаксиса метода. Следующий код вычисляет общую оценку для каждой Student
из исходных последовательностей, а затем вызывает Average()
метод в результатах этого запроса, чтобы вычислить среднюю оценку класса.
var studentQuery =
from student in students
let totalScore = student.Scores[0] + student.Scores[1] +
student.Scores[2] + student.Scores[3]
select totalScore;
double averageScore = studentQuery.Average();
Console.WriteLine($"Class average score = {averageScore}");
// Output:
// Class average score = 334.166666666667
Выполните преобразование или проекцию в операторе select
Обычно запрос создает последовательность, элементы которой отличаются от элементов в исходных последовательностях. Удалите или закомментируйте предыдущий цикл запроса и выполнения и замените его следующим кодом. Запрос возвращает последовательность строк (не Students
), и этот факт отражается в цикле foreach
.
IEnumerable<string> studentQuery =
from student in students
where student.Last == "Garcia"
select student.First;
Console.WriteLine("The Garcias in the class are:");
foreach (string s in studentQuery)
{
Console.WriteLine(s);
}
// Output:
// The Garcias in the class are:
// Cesar
// Debra
// Hugo
Код, приведенный ранее в этом пошаговом руководстве, указал, что средняя оценка класса составляет примерно 334. Чтобы создать последовательность Students
, общая оценка которой выше среднего по классу, вместе с их Student ID
, можно использовать анонимный тип в операторе select
.
var aboveAverageQuery =
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 aboveAverageQuery)
{
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