C# 14 и .NET 10: от поверхностного знания к рабочему (Extended)
Курс для программистов, которые C# «в целом знают», но на код-ревью и на собеседовании проваливаются на вопросах «почему так, а не иначе». Язык разбирается не как список конструкций, а как набор решений с ценой: значимые и ссылочные типы, память и сборщик мусора, равенство, null, сопоставление с образцом, строки и Span, обобщения, коллекции, LINQ, исключения, async и конкурентность, данные на границе процесса, рефлексия и генераторы кода. Затем пять юнитов на SOLID — каждый принцип как ответ на конкретную боль, а не как лозунг. Затем паттерны проектирования — порождающие, структурные, поведенческие и прикладные паттерны .NET — с обязательным разбором, когда паттерн вредит. Финал — тестируемость, разговор об архитектуре вслух и обзор нового в C# 13 и 14. Необязательная ветка «C# в приложении» доводит язык до работающего сервиса: хост и DI, Minimal API, HttpClient и фоновые задачи, EF Core, тесты, выпуск в Azure. Все примеры — в одной системе, бэкенде ENGAGE вымышленной компании Aldervane. C# 14 / .NET 10 LTS.
Материал и экзамен: русский · занятие ведёт ассистент
Как это устроено
Ассистент берёт материал курса с нашего сервера и ведёт занятие на вашем языке.
Экзамен сдаётся на сайте, без ассистента. Вопросы экзамена не переводятся: так решено для всех курсов.
Путь по курсу
· юнитов: 38порядок свободный · ступени ниже — тот порядок, который предлагает автор, а не условиеВид: карта · ступени
Ступень 1
~2 ч
Ступень 2
~2 ч
2 · по замыслу автора — после «Что на самом деле происходит, когда вы жмёте F5»
Значимые и ссылочные типы: стек, куча и упаковка
~2 ч
Ступень 3
~5,5 ч
по замыслу автора — после «Значимые и ссылочные типы: стек, куча и упаковка»
Ступень 4
~7,5 ч
5 · по замыслу автора — после «class, struct, record: чем объявлять данные»
Равенство, хеш и сравнение: контракт, который легко нарушить
~2 ч
6 · по замыслу автора — после «class, struct, record: чем объявлять данные»
null: nullable-типы, ссылочная нулевая аннотация и что с этим делать
~1,5 ч
28 · по замыслу автора — после любого из: «Значимые и ссылочные типы: стек, куча и упаковка», «Строки: неизменяемость, интернирование, StringBuilder, Span»
Span, Memory, ref struct и как мерить
~2 ч
8 · по замыслу автора — после «class, struct, record: чем объявлять данные»
Инкапсуляция и конструирование: объект, который нельзя собрать неправильно
~2 ч
Ступень 5
~8 ч
27 · по замыслу автора — после любого из: «class, struct, record: чем объявлять данные», «null: nullable-типы, ссылочная нулевая аннотация и что с этим делать»
Сопоставление с образцом, кортежи, деконструкция
~2 ч
9 · по замыслу автора — после «Инкапсуляция и конструирование: объект, который нельзя собрать неправильно»
Наследование и полиморфизм: virtual, override, new, sealed
~2 ч
11 · по замыслу автора — после «Инкапсуляция и конструирование: объект, который нельзя собрать неправильно»
Делегаты, лямбды, замыкания, события
~2 ч
15 · по замыслу автора — после «Инкапсуляция и конструирование: объект, который нельзя собрать неправильно»
Исключения: чем они являются и чем не являются
~2 ч
Ступень 6
~4 ч
10 · по замыслу автора — после «Наследование и полиморфизм: virtual, override, new, sealed»
Интерфейсы: контракт, явная реализация, DIM
~1,5 ч
16 · по замыслу автора — после «Делегаты, лямбды, замыкания, события»
async/await: что это и чего это не
~2,5 ч
Ступень 7
~8 ч
12 · по замыслу автора — после «Интерфейсы: контракт, явная реализация, DIM»
Обобщения: ограничения, реификация, вариантность
~2 ч
30 · по замыслу автора — после любого из: «Исключения: чем они являются и чем не являются», «async/await: что это и чего это не»
Данные на границе: JSON, файлы и потоки, даты и время
~2 ч
29 · по замыслу автора — после «async/await: что это и чего это не»
Конкурентность: lock, Lock, Interlocked, SemaphoreSlim, Channel, Parallel
~2 ч
17 · по замыслу автора — после «Интерфейсы: контракт, явная реализация, DIM»
SRP: одна причина для изменения
~2 ч
Ступень 8
~6 ч
31 · по замыслу автора — после любого из: «Что на самом деле происходит, когда вы жмёте F5», «Обобщения: ограничения, реификация, вариантность»
Метаданные: рефлексия, атрибуты, генераторы кода
~2 ч
13 · по замыслу автора — после «Обобщения: ограничения, реификация, вариантность»
Коллекции: выбор структуры данных и её цена
~2 ч
18 · по замыслу автора — после «SRP: одна причина для изменения»
OCP: открыт для расширения, закрыт для изменения
~2 ч
Ступень 9
~4 ч
14 · по замыслу автора — после «Коллекции: выбор структуры данных и её цена»
LINQ: отложенное выполнение, yield, IQueryable
~2 ч
19 · по замыслу автора — после «OCP: открыт для расширения, закрыт для изменения»
LSP: подтип обязан быть заменяемым
~2 ч
Ступень 10
~1,5 ч
20 · по замыслу автора — после «LSP: подтип обязан быть заменяемым»
ISP: клиента не заставляют зависеть от лишнего
~1,5 ч
Ступень 11
~2,5 ч
21 · по замыслу автора — после «ISP: клиента не заставляют зависеть от лишнего»
~2,5 ч
Ступень 12
~8 ч
22 · по замыслу автора — после «DIP и внедрение зависимостей»
Порождающие паттерны: кто и как создаёт объекты
~2 ч
23 · по замыслу автора — после «DIP и внедрение зависимостей»
Структурные паттерны: как собирать объекты в целое
~2 ч
24 · по замыслу автора — после «DIP и внедрение зависимостей», «Делегаты, лямбды, замыкания, события»
Поведенческие паттерны: кто кого вызывает и когда
~2 ч
33 · C# в приложении · по замыслу автора — после «DIP и внедрение зависимостей»
Хост, DI на практике, конфигурация, логирование
~2 ч
Ступень 13
~8 ч
25 · по замыслу автора — после 2 из: «Порождающие паттерны: кто и как создаёт объекты», «Структурные паттерны: как собирать объекты в целое», «Поведенческие паттерны: кто кого вызывает и когда»
Прикладные паттерны .NET: Repository, Options, CQRS, Result
~2 ч
34 · C# в приложении · по замыслу автора — после «Хост, DI на практике, конфигурация, логирование»
Minimal API: конвейер, привязка, валидация, ошибки
~2 ч
35 · C# в приложении · по замыслу автора — после «Хост, DI на практике, конфигурация, логирование», «async/await: что это и чего это не»
HttpClient, устойчивость и фоновые задачи
~2 ч
36 · C# в приложении · по замыслу автора — после «Хост, DI на практике, конфигурация, логирование», «LINQ: отложенное выполнение, yield, IQueryable»
~2 ч
Ступень 14
~4,5 ч
26 · по замыслу автора — после «Прикладные паттерны .NET: Repository, Options, CQRS, Result», «async/await: что это и чего это не»
Финал: тестируемость и разговор об архитектуре вслух
~2,5 ч
38 · C# в приложении · по замыслу автора — после «Minimal API: конвейер, привязка, валидация, ошибки»
Выпуск в Azure: App Service, секреты, managed identity
~2 ч
Ступень 15
~4 ч
32 · по замыслу автора — после «Финал: тестируемость и разговор об архитектуре вслух»
Что нового в C# 13 и 14 и как об этом спрашивают
~2 ч
37 · C# в приложении · по замыслу автора — после «Хост, DI на практике, конфигурация, логирование», «Финал: тестируемость и разговор об архитектуре вслух»
Тесты сервиса: от модуля до HTTP
~2 ч
Экзамен
закрыт · записаться на курс
финал · в браузере, без ассистента
Экзамен
Путь по курсу словами
- Ступень 1 · Что на самом деле происходит, когда вы жмёте F5 — ~2 ч
- Ступень 2 · Значимые и ссылочные типы: стек, куча и упаковка — по замыслу автора — после «Что на самом деле происходит, когда вы жмёте F5»; ~2 ч
- Ступень 3 · Память и время жизни: сборщик мусора, IDisposable, using — по замыслу автора — после «Значимые и ссылочные типы: стек, куча и упаковка»; ~2 ч
- Ступень 3 · class, struct, record: чем объявлять данные — по замыслу автора — после «Значимые и ссылочные типы: стек, куча и упаковка»; ~2 ч
- Ступень 3 · Строки: неизменяемость, интернирование, StringBuilder, Span — по замыслу автора — после «Значимые и ссылочные типы: стек, куча и упаковка»; ~1,5 ч
- Ступень 4 · Равенство, хеш и сравнение: контракт, который легко нарушить — по замыслу автора — после «class, struct, record: чем объявлять данные»; ~2 ч
- Ступень 4 · null: nullable-типы, ссылочная нулевая аннотация и что с этим делать — по замыслу автора — после «class, struct, record: чем объявлять данные»; ~1,5 ч
- Ступень 4 · Span, Memory, ref struct и как мерить — по замыслу автора — после любого из: «Значимые и ссылочные типы: стек, куча и упаковка», «Строки: неизменяемость, интернирование, StringBuilder, Span»; ~2 ч
- Ступень 4 · Инкапсуляция и конструирование: объект, который нельзя собрать неправильно — по замыслу автора — после «class, struct, record: чем объявлять данные»; ~2 ч
- Ступень 5 · Сопоставление с образцом, кортежи, деконструкция — по замыслу автора — после любого из: «class, struct, record: чем объявлять данные», «null: nullable-типы, ссылочная нулевая аннотация и что с этим делать»; ~2 ч
- Ступень 5 · Наследование и полиморфизм: virtual, override, new, sealed — по замыслу автора — после «Инкапсуляция и конструирование: объект, который нельзя собрать неправильно»; ~2 ч
- Ступень 5 · Делегаты, лямбды, замыкания, события — по замыслу автора — после «Инкапсуляция и конструирование: объект, который нельзя собрать неправильно»; ~2 ч
- Ступень 5 · Исключения: чем они являются и чем не являются — по замыслу автора — после «Инкапсуляция и конструирование: объект, который нельзя собрать неправильно»; ~2 ч
- Ступень 6 · Интерфейсы: контракт, явная реализация, DIM — по замыслу автора — после «Наследование и полиморфизм: virtual, override, new, sealed»; ~1,5 ч
- Ступень 6 · async/await: что это и чего это не — по замыслу автора — после «Делегаты, лямбды, замыкания, события»; ~2,5 ч
- Ступень 7 · Обобщения: ограничения, реификация, вариантность — по замыслу автора — после «Интерфейсы: контракт, явная реализация, DIM»; ~2 ч
- Ступень 7 · Данные на границе: JSON, файлы и потоки, даты и время — по замыслу автора — после любого из: «Исключения: чем они являются и чем не являются», «async/await: что это и чего это не»; ~2 ч
- Ступень 7 · Конкурентность: lock, Lock, Interlocked, SemaphoreSlim, Channel, Parallel — по замыслу автора — после «async/await: что это и чего это не»; ~2 ч
- Ступень 7 · SRP: одна причина для изменения — по замыслу автора — после «Интерфейсы: контракт, явная реализация, DIM»; ~2 ч
- Ступень 8 · Метаданные: рефлексия, атрибуты, генераторы кода — по замыслу автора — после любого из: «Что на самом деле происходит, когда вы жмёте F5», «Обобщения: ограничения, реификация, вариантность»; ~2 ч
- Ступень 8 · Коллекции: выбор структуры данных и её цена — по замыслу автора — после «Обобщения: ограничения, реификация, вариантность»; ~2 ч
- Ступень 8 · OCP: открыт для расширения, закрыт для изменения — по замыслу автора — после «SRP: одна причина для изменения»; ~2 ч
- Ступень 9 · LINQ: отложенное выполнение, yield, IQueryable — по замыслу автора — после «Коллекции: выбор структуры данных и её цена»; ~2 ч
- Ступень 9 · LSP: подтип обязан быть заменяемым — по замыслу автора — после «OCP: открыт для расширения, закрыт для изменения»; ~2 ч
- Ступень 10 · ISP: клиента не заставляют зависеть от лишнего — по замыслу автора — после «LSP: подтип обязан быть заменяемым»; ~1,5 ч
- Ступень 11 · DIP и внедрение зависимостей — по замыслу автора — после «ISP: клиента не заставляют зависеть от лишнего»; ~2,5 ч
- Ступень 12 · Порождающие паттерны: кто и как создаёт объекты — по замыслу автора — после «DIP и внедрение зависимостей»; ~2 ч
- Ступень 12 · Структурные паттерны: как собирать объекты в целое — по замыслу автора — после «DIP и внедрение зависимостей»; ~2 ч
- Ступень 12 · Поведенческие паттерны: кто кого вызывает и когда — по замыслу автора — после «DIP и внедрение зависимостей», «Делегаты, лямбды, замыкания, события»; ~2 ч
- Ступень 12 · Хост, DI на практике, конфигурация, логирование — по замыслу автора — после «DIP и внедрение зависимостей»; необязательный; ~2 ч
- Ступень 13 · Прикладные паттерны .NET: Repository, Options, CQRS, Result — по замыслу автора — после 2 из: «Порождающие паттерны: кто и как создаёт объекты», «Структурные паттерны: как собирать объекты в целое», «Поведенческие паттерны: кто кого вызывает и когда»; ~2 ч
- Ступень 13 · Minimal API: конвейер, привязка, валидация, ошибки — по замыслу автора — после «Хост, DI на практике, конфигурация, логирование»; необязательный; ~2 ч
- Ступень 13 · HttpClient, устойчивость и фоновые задачи — по замыслу автора — после «Хост, DI на практике, конфигурация, логирование», «async/await: что это и чего это не»; необязательный; ~2 ч
- Ступень 13 · EF Core без иллюзий — по замыслу автора — после «Хост, DI на практике, конфигурация, логирование», «LINQ: отложенное выполнение, yield, IQueryable»; необязательный; ~2 ч
- Ступень 14 · Финал: тестируемость и разговор об архитектуре вслух — по замыслу автора — после «Прикладные паттерны .NET: Repository, Options, CQRS, Result», «async/await: что это и чего это не»; ~2,5 ч
- Ступень 14 · Выпуск в Azure: App Service, секреты, managed identity — по замыслу автора — после «Minimal API: конвейер, привязка, валидация, ошибки»; необязательный; ~2 ч
- Ступень 15 · Что нового в C# 13 и 14 и как об этом спрашивают — по замыслу автора — после «Финал: тестируемость и разговор об архитектуре вслух»; ~2 ч
- Ступень 15 · Тесты сервиса: от модуля до HTTP — по замыслу автора — после «Хост, DI на практике, конфигурация, логирование», «Финал: тестируемость и разговор об архитектуре вслух»; необязательный; ~2 ч
Программа · 38 юнитов
| 1 | Что на самом деле происходит, когда вы жмёте F5 Описать путь от исходника до машинного кода: Roslyn → IL и метаданные → JIT (tier0 → tier1) → машинный код, и что меняют Debug и Release | ~2 ч |
| 2 | Значимые и ссылочные типы: стек, куча и упаковка Предсказывать поведение кода при присваивании и передаче в метод для значимых и ссылочных типов, включая структуры в коллекциях и кортежи | ~2 ч |
| 3 | Память и время жизни: сборщик мусора, IDisposable, using Объяснить поколенческую сборку и почему её цена определяется выжившими, а не мёртвыми объектами | ~2 ч |
| 4 | class, struct, record: чем объявлять данные Выбирать между class, struct, record class, record struct и readonly record struct по вопросу «значение или сущность», а не по привычке | ~2 ч |
| 5 | Равенство, хеш и сравнение: контракт, который легко нарушить Предсказывать, что сравнивает == и что сравнивает Equals, по статическому и настоящему типу | ~2 ч |
| 6 | null: nullable-типы, ссылочная нулевая аннотация и что с этим делать Различать Nullable<T> и nullable reference types: что делает компилятор, что делает рантайм (особая упаковка) и что не делает никто | ~1,5 ч |
| 7 | Сопоставление с образцом, кортежи, деконструкция Переписывать цепочки if/else с is и приведениями в switch-выражение и объяснять, почему порядок веток — часть смысла | ~2 ч |
| 8 | Span, Memory, ref struct и как мерить Объяснять, что такое Span<T> и ReadOnlySpan<T>, и предсказывать, где срез даёт окно, а где копию | ~2 ч |
| 9 | Строки: неизменяемость, интернирование, StringBuilder, Span Объяснять, что неизменяемость string даёт (безопасная передача, потокобезопасное чтение, надёжный ключ, интернирование) и чего не даёт (кэширования хеша, возможности стереть секрет) | ~1,5 ч |
| 10 | Инкапсуляция и конструирование: объект, который нельзя собрать неправильно Объяснять инкапсуляцию через инварианты и проектировать тип, в котором невалидное состояние невыразимо | ~2 ч |
| 11 | Наследование и полиморфизм: virtual, override, new, sealed Точно предсказывать, какая реализация вызовется при virtual/override, new и new virtual, через базовую и производную ссылку | ~2 ч |
| 12 | Интерфейсы: контракт, явная реализация, DIM Проектировать интерфейс от потребителя и класть его рядом с потребителем, чтобы развернуть зависимость | ~1,5 ч |
| 13 | Делегаты, лямбды, замыкания, события Объяснить устройство делегата (Target, Method, список вызова) и предсказать результат многоадресного вызова, в том числе с исключением | ~2 ч |
| 14 | Обобщения: ограничения, реификация, вариантность Объяснить реификацию обобщений: отдельный машинный код для значимых T, общий для ссылочных, реальный тип в рантайме | ~2 ч |
| 15 | Метаданные: рефлексия, атрибуты, генераторы кода Объяснить, что такое метаданные сборки и что делает рефлексия (Type, GetProperties, PropertyInfo.GetValue), и назвать её цену: поиск членов, позднее связывание, упаковка, негарантированный порядок | ~2 ч |
| 16 | Коллекции: выбор структуры данных и её цена Выбирать коллекцию по горячей операции, изменяемости, нужному порядку и числу писателей, а не по привычке | ~2 ч |
| 17 | LINQ: отложенное выполнение, yield, IQueryable Объяснить отложенное выполнение как вытягивание элементов по одному с конца цепочки и предсказывать его эффекты: повторную работу, изменения источника, захваченные переменные | ~2 ч |
| 18 | Исключения: чем они являются и чем не являются Решать по контракту метода, что является исключением, а что — ожидаемым результатом | ~2 ч |
| 19 | Данные на границе: JSON, файлы и потоки, даты и время Настроить System.Text.Json для внешнего контракта: один экземпляр JsonSerializerOptions, политика имён, строгость (required, RespectNullableAnnotations, RespectRequiredConstructorParameters из .NET 9), полиморфизм | ~2 ч |
| 20 | async/await: что это и чего это не Объяснить, почему async освобождает поток на время ожидания, а не создаёт новый, и чем масштабируемость отличается от скорости | ~2,5 ч |
| 21 | Конкурентность: lock, Lock, Interlocked, SemaphoreSlim, Channel, Parallel Объяснить гонку read-modify-write и предсказать, что произойдёт с общим полем, которое меняют несколько потоков | ~2 ч |
| 22 | SRP: одна причина для изменения Сформулировать SRP через причину для изменения: один актор, приходящий с требованиями | ~2 ч |
| 23 | OCP: открыт для расширения, закрыт для изменения Объяснить OCP через давление: одно изменение требований — правки в нескольких работающих модулях | ~2 ч |
| 24 | LSP: подтип обязан быть заменяемым Сформулировать LSP через ожидания вызывающего кода и контракт базы: предусловия, постусловия, инварианты, история и исключения | ~2 ч |
| 25 | ISP: клиента не заставляют зависеть от лишнего Объяснить ISP через механику зависимости: изменение толстого интерфейса пересобирает и ломает клиентов, которым изменившийся член не нужен | ~1,5 ч |
| 26 | DIP и внедрение зависимостей Сформулировать обе части DIP и показать, почему вторая (контракт на языке потребителя и у потребителя) важнее | ~2,5 ч |
| 27 | Порождающие паттерны: кто и как создаёт объекты Называть давление, которое снимает каждый порождающий паттерн, и случаи, когда хватает new | ~2 ч |
| 28 | Структурные паттерны: как собирать объекты в целое Различать Adapter, Decorator, Facade и Proxy по намерению и по тому, меняется ли интерфейс | ~2 ч |
| 29 | Поведенческие паттерны: кто кого вызывает и когда Узнавать давление, порождающее Strategy, Template Method, Observer, Command, State и Chain of Responsibility, на коде ENGAGE | ~2 ч |
| 30 | Прикладные паттерны .NET: Repository, Options, CQRS, Result Аргументированно решать, нужен ли свой репозиторий поверх EF Core, и называть, чем обедняет общий IRepository<T> | ~2 ч |
| 31 | Что нового в C# 13 и 14 и как об этом спрашивают Отнести возможность к версии языка: раньше C# 13, C# 13 (.NET 9) или C# 14 (.NET 10) — и не выдавать старое за новое | ~2 ч |
| 32 | Финал: тестируемость и разговор об архитектуре вслух Связывать трудности тестирования с конкретными дефектами проектирования: new внутри метода, статика, DateTime.Now, new HttpClient(), файловая система | ~2,5 ч |
| 33 | Хост, DI на практике, конфигурация, логирование Объяснить, что регистрируют Host.CreateApplicationBuilder и WebApplication.CreateBuilder, и собрать Program.cs как composition root с областями для Scoped-сервисов | ~2 ч |
| 34 | Minimal API: конвейер, привязка, валидация, ошибки Предсказать источник каждого параметра обработчика (маршрут, строка запроса, тело, DI, [AsParameters]) и ответ на ошибку привязки | ~2 ч |
| 35 | HttpClient, устойчивость и фоновые задачи Объяснить, почему new HttpClient() на каждый запрос исчерпывает порты, а вечный статический клиент не видит смены DNS, и выбрать между IHttpClientFactory и долгоживущим клиентом с PooledConnectionLifetime | ~2 ч |
| 36 | EF Core без иллюзий Определять, какая часть LINQ-запроса уйдёт в SQL, а какая выполнится в памяти: Expression против Func, ToList в середине цепочки, непереводимые вызовы методов | ~2 ч |
| 37 | Тесты сервиса: от модуля до HTTP Объяснить, какой класс дефектов ловит каждый уровень тестов сервиса, и выбрать между пирамидой и ромбом по тому, где живут ошибки | ~2 ч |
| 38 | Выпуск в Azure: App Service, секреты, managed identity Опубликовать сервис ENGAGE (dotnet publish) и выбрать режим: framework-dependent, self-contained или ReadyToRun — с ценой каждого | ~2 ч |
