Навчальний простір / C#
Моделювання та аналіз програмного забезпечення

Аналіз моделі: LINQ, часові дані та асинхронні операції

LINQ потребує розуміння відкладеного виконання й матеріалізації.

Зміст матеріалу

Що ви опануєте

Ви навчитеся формувати запити до колекцій, пояснювати відкладене виконання LINQ і представляти операцію, що завершується пізніше. Приклад — звіт про бронювання пристроїв. Перед використанням розберемо IEnumerable, lambda, Task, await і cancellation.

Запит до колекції

LINQ — засоби C# і .NET для виразного опису запитів до даних. Where фільтрує, Select перетворює, OrderBy упорядковує. Lambda — короткий вираз функції, наприклад x => x.Active. IEnumerable<T> описує можливість перебирати елементи T; він не гарантує збереження результату чи незмінність джерела.

csharp
using System;
using System.Linq;

var reservations = new[] {
    new Reservation(Guid.NewGuid(), "Проєктор", true),
    new Reservation(Guid.NewGuid(), "Мікроскоп", false),
    new Reservation(Guid.NewGuid(), "Камера", true)
};
var activeNames = reservations
    .Where(r => r.Active)
    .OrderBy(r => r.EquipmentName)
    .Select(r => r.EquipmentName)
    .ToList(); // Матеріалізуємо результат зараз.
Console.WriteLine(string.Join(", ", activeNames));

public sealed record Reservation(Guid Id, string EquipmentName, bool Active);

record надає зручне представлення даних із value-oriented операціями; посилальний record не стає глибоко незмінюваним автоматично для всіх вкладених полів. Тут поля рядка, Guid і bool використовуються як простий snapshot. Очікується дві активні назви, упорядковані comparer середовища. Для відтворюваного технічного порядку можна явно задати StringComparer.Ordinal.

Відкладене виконання

Багато LINQ операторів створюють опис запиту, який виконується при переборі. Якщо між створенням запиту та ToList змінити джерело, результат може відобразити новий стан. Повторний перебір може повторити роботу. Матеріалізація зберігає отримані результати, але створює витрати пам’яті й не завжди дає глибоку копію об’єктів.

Використання LINQ до бази даних через IQueryable має іншу межу виконання: provider перекладає підтримувані вирази в запит. Не кожна C# функція може бути перекладена. Потрібно знати, де виконується фільтрація та коли дані завантажуються. Для цієї лекції працюємо з колекцією в пам’яті, щоб не приховувати додаткових припущень.

Часові дані

DateTimeOffset представляє дату й час із offset від UTC. Він дозволяє позначити момент, але offset не є повною моделлю часового поясу з його правилами переходів. Для бронювання потрібно погодити зберігання моментів і відображення локального часу. Інтервал доцільно визначати як напіввідкритий: початок включено, кінець виключено.

Два інтервали перетинаються, якщо startA<endBstart_A<end_B і startB<endAstart_B<end_A. Для суміжних інтервалів 10:00–11:00 та 11:00–12:00 перетину немає. Це правило слід використовувати узгоджено в моделі, запитах і тестах. Зміна <= створює інший контракт на границі.

Асинхронність

Task<T> представляє результат операції, який може бути доступний пізніше. Async дозволяє функції використовувати await, а await призупиняє її продовження до завершення операції. Очікування асинхронного I/O не потребує блокувати потік на весь час. Async сам по собі не робить будь-яку роботу паралельною чи швидшою.

csharp
using System;
using System.Threading;
using System.Threading.Tasks;

using var cancellation = new CancellationTokenSource();
int count = await LoadCountAsync(cancellation.Token);
Console.WriteLine(count); // 3.

static async Task<int> LoadCountAsync(CancellationToken token)
{
    // Навчальна імітація очікування зовнішніх даних.
    await Task.Delay(100, token);
    return 3;
}

CancellationToken передає сигнал скасування. Операція має співпрацювати з ним; передача token не перериває довільний код автоматично. using викликає Dispose для джерела після завершення області. Task.Delay тут імітує затримку й не є мережею або тестом її надійності.

Помилки й моделі стану

Операція завантаження може бути в Loading, Ready, Error або Cancelled. Виняток асинхронної Task спостерігається при await. Скасування часто передається OperationCanceledException. Не зводьте його до звичайної помилки без потреби, оскільки користувач міг навмисно закрити сценарій. Не залишайте Task без спостереження там, де її результат важливий.

Блокування через .Result або .Wait() може затримувати UI й створювати проблеми з контекстом виконання. Передавайте асинхронний контракт через потрібні рівні. Для CPU-heavy роботи окремо оцінюють використання потоків; ця тема має інші обмеження, ніж очікування мережі.

Типові помилки та практика

Помилки: повторне виконання дорогого LINQ запиту, припущення глибокої копії ToList, змішані часові пояси, async без пояснення результату й поглинання всіх винятків. Почніть налагодження з меж виконання: коли отримано дані, коли матеріалізовано запит, де очікується Task і хто скасовує операцію.

Практичне осмислення

Створіть запит активних бронювань на майбутнє, визначивши джерело часу як параметр. Поясніть, коли виконуватиметься запит. Складіть таблицю станів завантаження та поведінки при скасуванні. Чому інтерфейс не повинен показувати Error лише через закриття екрана користувачем?

Відкладене виконання та асинхронна межа

LINQ-запит Where зазвичай задає послідовність обчислення, яку буде виконано під час переліку. Якщо початкова колекція змінилася до ToList, результат може відображати новий стан. ToList матеріалізує поточний результат у новий список; це має вартість часу й пам’яті. Для змінюваних елементів новий список усе ще може містити посилання на ті самі об’єкти.

IEnumerable<T> описує можливість послідовного читання. IQueryable<T> додатково представляє запит для провайдера, наприклад бази даних. Методи й вирази можуть переводитися у SQL, тому доступні операції та момент виконання залежать від провайдера. Приклади цієї лекції працюють із колекціями в пам’яті; їхню поведінку не слід автоматично переносити на всі ORM-запити.

Task представляє майбутнє завершення операції. Await дозволяє продовжити метод після її завершення без синхронного очікування потоком у типовому I/O-сценарії. Асинхронність не робить доступ до спільної колекції атомарним. Дві операції можуть прочитати однаковий стан і прийняти несумісні рішення; потрібне узгодження сховища.

CancellationToken повідомляє про запит скасування, який операція повинна підтримати. Передавайте його через межі сервісів і обробляйте скасування відповідно до контракту. Після частково виконаної зовнішньої дії треба знати, які зміни вже підтверджені. У власному аналізі розділіть читання, рішення та запис і вкажіть, де проходить атомарна межа. Така схема пояснює, чому async і цілісність мають окремі механізми.

Схема процесу

Аналіз моделі: LINQ, часові дані та асинхронні операціїОберіть елемент, щоб побачити пояснення

Уточнюємо колекцію й момент даних.

Самоперевірка

Перевірте розуміння
Що означає await для асинхронного I/O?
Як обґрунтувати відповідь своїми словами?
Await організує продовження після завершення Task. Спосіб виконання роботи залежить від самої операції.

Підсумок

LINQ потребує розуміння відкладеного виконання й матеріалізації. Асинхронність описує очікування результату, а cancellation — співпрацю зі скасуванням. Часові правила та стани операції роблять аналіз поведінки відтворюваним.

Джерела

Опрацювали матеріал?

Збережіть цей крок у своєму прогресі.

← Повернутися до дисципліни
© 2026 Анастасія Іскандарова-МалаЕлектронний навчальний посібник · ДДТУ

Пошук у посібнику

Спробуйте «C++», «RAII» або «бази даних».