Вопросы на собеседовании: .NET-разработчик
100 реальных вопросов с образцовыми ответами и пояснениями для уровня Junior .NET-разработчик.
Смотреть пример резюме: .NET-разработчик →Тренировка флешкарточками
Интервальное повторение · Hunter Pass
Вопросы
C# является языком программирования, .NET является платформой и runtime-экосистемой, а CLR является средой выполнения управляемого .NET-кода.
- Компилятор C# обычно создаёт Common Intermediate Language и метаданные сборки.
- CLR загружает сборки, проверяет и компилирует методы для выполнения и предоставляет runtime-сервисы.
- .NET также включает базовые библиотеки, SDK, инструменты сборки и фреймворки приложений.
Зачем это спрашивают: Интервьюер проверяет, различаете ли вы язык, платформу и её среду выполнения.
JIT-компилятор переводит промежуточный язык метода в нативный машинный код, когда метод требуется во время выполнения.
- Сгенерированный код предназначен для текущей операционной системы и архитектуры процессора.
- Метод может быть скомпилирован при первом вызове и затем переиспользоваться в течение жизни процесса.
- .NET также поддерживает ahead-of-time варианты, но JIT остаётся основной моделью выполнения CLR.
Зачем это спрашивают: Интервьюер оценивает базовое понимание того, как скомпилированный C# доходит до нативного выполнения.
Переменная value type содержит само значение, а переменная reference type содержит ссылку на объект.
- Присваивание value type обычно копирует содержащееся значение.
- Присваивание reference type копирует ссылку, поэтому обе переменные могут указывать на один объект.
- Structs, enums и встроенные числовые типы являются value types, а classes, arrays и delegates являются reference types.
Зачем это спрашивают: Сильный ответ объясняет семантику присваивания, а не сводит различие только к стеку и куче.
Value type хранится inline там, где находится содержащее его хранилище, поэтому это не всегда стек.
- Локальное значение может находиться в стеке, если runtime выбирает такое представление.
- Поле value type внутри класса хранится как часть объекта класса в managed heap.
- Boxing также помещает копию value type внутрь объекта в куче.
Зачем это спрашивают: Интервьюер проверяет, основана ли ваша модель value types на семантике, а не на упрощённом правиле хранения.
Struct является value type с копированием значения, а class является reference type с идентичностью по ссылке.
- Присваивание struct копирует его значение, а присваивание class копирует ссылку на тот же экземпляр.
- Struct не может наследоваться от другого class или struct, но может реализовывать interfaces.
- Небольшие неизменяемые данные часто подходят для struct, а сущности с идентичностью и общей мутацией обычно подходят для classes.
Зачем это спрашивают: Интервьюер оценивает, связываете ли вы категорию типа с копированием, наследованием и назначением модели.
Boxing преобразует value type в object или interface reference, помещая копию значения в managed object.
- Он может произойти неявно при присваивании value type переменной object.
- Boxing выделяет память и копирует значение, поэтому частый boxing создаёт дополнительные расходы памяти и времени.
- Generic-коллекции вроде List<int> не делают boxing каждого числа, в отличие от старых коллекций на основе object.
Зачем это спрашивают: Сильный ответ раскрывает преобразование, выделение памяти и причину, по которой generics сокращают boxing.
Unboxing извлекает value type из boxed object через явное приведение к точному упакованному типу значения.
- Объект должен содержать boxed-значение запрошенного типа.
- Null reference вызывает NullReferenceException, а несовместимый boxed-тип вызывает InvalidCastException.
- Unboxing не выполняет обычное числовое преобразование, поэтому boxed int нельзя напрямую распаковать как long.
Зачем это спрашивают: Интервьюер проверяет, понимаете ли вы unboxing как точное типизированное извлечение, а не универсальное приведение.
Среди распространённых встроенных категорий C# есть числовые, логические, символьные, строковые, object, enum, tuple, array и nullable-типы.
- Целые типы различаются знаком и шириной, а float, double и decimal предназначены для разных числовых задач.
- bool представляет true или false, а char представляет одну кодовую единицу UTF-16.
- string является reference type с неизменяемыми экземплярами.
Зачем это спрашивают: Интервьюер оценивает базовое владение системой типов и знание семантики string и char.
decimal предпочтителен для финансовых вычислений в десятичной системе, где денежные дроби должны представляться предсказуемо.
- double использует двоичную плавающую точку, обычно работает быстрее и имеет намного больший диапазон порядка.
- Многие десятичные дроби нельзя точно представить в double, что приводит к заметным эффектам округления.
- Для обоих типов бизнес-правила всё равно должны явно задавать политику округления.
Зачем это спрашивают: Сильный ответ выбирает числовой тип по представлению и требованиям предметной области, а не только по размеру.
var просит компилятор вывести статический тип локальной переменной из инициализатора.
- Выведенный тип фиксируется при компиляции и не делает переменную динамически типизированной.
- Инициализатор обязателен, потому что компилятору нужно выражение для вывода типа.
- var удобен, когда тип очевиден или громоздок, но в других случаях явный тип яснее передаёт намерение.
Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы compile-time вывод от динамической типизации.
Nullable value type T? может представлять либо значение T, либо отсутствие значения.
- int? является сокращением для Nullable<int>.
- HasValue и Value открывают состояние, хотя Value выбрасывает исключение при отсутствии значения.
- Оператор ?? задаёт fallback без ручной проверки HasValue.
Зачем это спрашивают: Интервьюер оценивает понимание того, как value types явно представляют отсутствие.
Nullable reference types являются compile-time аннотациями и анализом, различающим ссылки с намерением non-null и ссылки, которым разрешён null.
- Включённый nullable-контекст трактует string как намерение non-null, а string? разрешает null.
- Компилятор выдаёт предупреждения, если код может разыменовать null или нарушить эти аннотации.
- Возможность не добавляет runtime-проверки и не создаёт другой CLR-тип ссылки.
Зачем это спрашивают: Сильный ответ объясняет и пользу статической безопасности, и отсутствие новой runtime-семантики.
Операторы ?., ?? и ! выражают разные части обработки null в C#.
- ?. прекращает доступ к члену и возвращает null, если receiver равен null.
- ?? возвращает правый операнд, если левый равен null.
- Постфиксный ! подавляет nullable-предупреждения, но не выполняет runtime-проверку и может скрыть реальный null-баг.
Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы безопасное распространение, выбор fallback и подавление предупреждения.
Конструктор инициализирует новый экземпляр класса и устанавливает состояние, необходимое этому экземпляру.
- Он имеет имя класса, не имеет return type и может принимать параметры.
- Конструкторы можно перегружать и делегировать другому конструктору через this или базовому через base.
- Если instance constructor не объявлен, компилятор может предоставить конструктор без параметров, когда это разрешают правила языка.
Зачем это спрашивают: Интервьюер оценивает базовое знание создания и инициализации объектов.
Эти access modifiers управляют местами, из которых доступен тип или член.
- public разрешает доступ везде, где доступен содержащий тип, а private ограничивает доступ содержащим типом.
- protected разрешает доступ через объявляющий класс и его производные классы.
- internal разрешает доступ из кода той же сборки.
Зачем это спрашивают: Интервьюер проверяет понимание распространённых границ видимости для инкапсуляции.
Поле является хранилищем, объявленным непосредственно в типе, а свойство открывает методы доступа через синтаксис, похожий на поле.
- Свойство может проверять, вычислять или ограничивать чтение и запись через accessors get, set или init.
- Auto-property позволяет компилятору создать скрытое backing storage.
- Публичные API обычно предпочитают свойства, потому что их реализация развивается без изменения синтаксиса доступа к члену.
Зачем это спрашивают: Сильный ответ отличает прямое хранилище от инкапсулированного контракта на основе accessors.
Accessor set разрешает присваивание после создания объекта, а init ограничивает присваивание инициализацией объекта и связанными с конструированием контекстами.
- Оба можно использовать через object initializer.
- init помогает создавать объекты, публичное состояние которых не должно меняться после инициализации.
- Readonly field является другой конструкцией со своими правилами присваивания, а не accessor свойства.
Зачем это спрашивают: Интервьюер оценивает понимание базовой управляемой изменяемости в современном C#.
Instance member принадлежит конкретному объекту, а static member принадлежит самому типу.
- Instance methods получают доступ к текущему объекту через this.
- Static methods не имеют instance receiver и напрямую обращаются только к static members.
- Static-состояние общее для вызывающих сторон процесса, поэтому его мутация требует осознанного проектирования.
Зачем это спрашивают: Интервьюер проверяет понимание принадлежности членов и общей природы static-состояния.
virtual позволяет производным классам заменить полиморфную реализацию метода, а override предоставляет эту замену.
- Вызов через ссылку базового типа выбирает override по runtime-типу объекта.
- new скрывает унаследованный член по compile-time типу ссылки вместо участия в том же virtual slot.
- Компилятор предупреждает о случайном скрытии, поэтому выбор следует делать явно.
Зачем это спрашивают: Сильный ответ отличает runtime-полиморфное переопределение от compile-time скрытия члена.
Abstract class нельзя создать напрямую, и он может определять члены, которые обязаны реализовать производные классы.
- Abstract members не имеют реализации в базовом классе и требуют override в concrete derived class.
- Abstract class также может содержать реализованные методы, поля, конструкторы и состояние.
- Concrete class обязан предоставить всё оставшееся abstract-поведение до создания экземпляров.
Зачем это спрашивают: Интервьюер оценивает базовую терминологию наследования и назначение незавершённых базовых типов.
Закрытые вопросы
- 21
Что такое List<T> и когда он полезен?
- 22
Чем массив отличается от List<T>?
- 23
Что такое Dictionary<TKey, TValue>?
- 24
Что такое IEnumerable<T>?
- 25
Что foreach делает с коллекцией?
- 26
Что такое LINQ?
linq - 27
Что такое deferred execution в LINQ?
linq - 28
Чем Where отличается от Select в LINQ?
linq - 29
Чем First, FirstOrDefault, Single и SingleOrDefault отличаются друг от друга?
- 30
Почему Any обычно яснее, чем Count() > 0 в LINQ?
linq - 31
Для чего используются try, catch и finally?
- 32
Почему не следует по умолчанию перехватывать Exception вместо конкретных типов?
error-handling - 33
Для чего нужен блок finally?
- 34
Чем throw отличается от throw ex внутри catch?
- 35
Когда следует создать собственный тип исключения?
error-handling - 36
Что означают async и await в C#?
async - 37
Что такое Task в .NET?
async - 38
Почему асинхронные методы обычно должны возвращать Task или Task<T>, а не void?
async - 39
Как получить результат из Task<T>?
async - 40
Как исключения ведут себя в async-методе, возвращающем Task?
asyncerror-handling - 41
Что такое interface в C#?
types - 42
Чем interface отличается от abstract class?
types - 43
Что такое generics в C#?
generics - 44
Для чего используются generic constraints?
generics - 45
Что такое generic method?
generics - 46
Что такое delegate в C#?
delegationdelegates - 47
Что такое delegates Action, Func и Predicate?
delegationdelegates - 48
Что такое event в C#?
delegates - 49
Что делает garbage collector .NET?
gc - 50
Почему GC .NET использует поколения?
- 51
Как посчитать, сколько раз каждое слово встречается в списке строк?
- 52
Как удалить повторяющиеся ID из последовательности, сохранив порядок первого появления?
- 53
Пользователь вводит возраст текстом. Как преобразовать его без исключения при неверном вводе?
- 54
Nullable reference warnings показывают, что customer.Address может быть null. Как исправить код?
fundamentalsnullable - 55
Код удаляет элементы из List<T> внутри foreach и падает во время выполнения. Как его исправить?
- 56
Как сделать dictionary адресов email, который считает разный регистр одним ключом?
- 57
Как обрабатывать заявки поддержки в том же порядке, в котором они поступили?
concurrency - 58
Как проверить правильную вложенность скобок в выражении?
validation - 59
ToDictionary выбрасывает исключение, потому что источник содержит повторяющиеся коды товаров. Как обработать данные?
- 60
Метод получает коллекцию, но должен только перебирать её. Какой тип параметра выбрать?
- 61
Как через LINQ вернуть имена активных пользователей не младше 18 лет?
linq - 62
Как отсортировать заказы сначала по новой дате, а затем по номеру заказа?
- 63
Как через LINQ посчитать общую сумму продаж для каждого клиента?
linq - 64
В памяти есть клиенты и заказы. Как получить каждый заказ вместе с именем клиента?
memory - 65
Каждый отдел содержит список сотрудников. Как вернуть одну плоскую последовательность сотрудников?
- 66
First выбрасывает исключение, когда подходящий пользователь не найден. Как изменить поиск?
- 67
LINQ query создан, исходный список изменился, и поздний результат тоже неожиданно изменился. Как это исправить?
querieslinq - 68
Метод несколько раз перебирает одну LINQ-последовательность и повторяет дорогую работу. Как его улучшить?
linq - 69
Как проверить наличие хотя бы одного просроченного счёта, не подсчитывая все совпадения?
- 70
EF Core query загружает все товары до фильтрации по категории. Как это исправить?
queriesorm - 71
Как написать асинхронный метод, который загружает пользователя и возвращает отсутствие значения, если пользователь не найден?
async - 72
Controller вызывает SaveAsync без await и возвращает успех до завершения сохранения. Как это исправить?
async - 73
Код использует task.Result внутри async request path и иногда зависает или блокирует threads. Что изменить?
asyncconcurrency - 74
Три независимых API-вызова ожидаются один за другим. Как сократить общее время ожидания?
asyncapi - 75
Когда две async-операции должны остаться последовательными вместо Task.WhenAll?
async - 76
Как передать отмену из ASP.NET Core request в EF Core?
resilienceaspnetorm - 77
Как добавить двухсекундный timeout к async-операции, которая уже поддерживает отмену?
resilienceasync - 78
Один task в Task.WhenAll завершается ошибкой. Как обработать объединённую операцию?
async - 79
Созданный асинхронно stream нужно освободить после использования. Как написать метод?
async - 80
Сервис создаёт новый HttpClient для каждого запроса и начинает падать под нагрузкой. Как это исправить?
debugging - 81
Select возвращает IEnumerable<Task<Product>> при загрузке деталей товаров. Как получить товары?
async - 82
Catch block игнорирует любой Exception и возвращает null. Как его улучшить?
fundamentalserror-handling - 83
Catch block использует throw ex, и stack trace указывает на строку catch. Как это исправить?
debugging - 84
Как гарантировать закрытие file stream, если парсинг выбросит исключение?
error-handling - 85
Repository выбрасывает SqlException прямо в controller. Как не допустить утечки деталей базы?
database - 86
Как повторять операцию только для конкретного временного исключения?
resilienceerror-handling - 87
Поиск часто неуспешен из-за неверного пользовательского ввода. Должен ли метод выбрасывать исключение?
error-handling - 88
Где логировать exception, чтобы он не записывался несколько раз?
error-handling - 89
ASP.NET Core endpoint получает невалидный ввод. Как не возвращать общий 500 error?
genericsendpointsaspnet - 90
Как сделать order service тестируемым, если сейчас он сам создаёт email sender?
- 91
Как реализовать payment service с переключением между оплатой картой и банковским переводом?
- 92
Класс реализует большой interface, но половина методов выбрасывает NotSupportedException. Как его улучшить?
types - 93
Как создать переиспользуемый result type, который содержит данные или сообщение об ошибке?
- 94
Как написать generic method, который возвращает первый подходящий элемент или сообщает об отсутствии?
generics - 95
Как реализовать generic in-memory cache со строковыми ключами?
cachingmemorygenerics - 96
Метод приводит каждый object к Customer и падает при появлении другого типа. Как это исправить?
- 97
Как десериализовать разные тела API-ответов одним переиспользуемым методом?
api - 98
Как ограничить generic method, который сравнивает entities по целочисленному ID?
generics - 99
Как добавить переиспользуемую проверку IsNullOrEmpty для IEnumerable<T>?
- 100
Как протестировать service, который зависит от interface product repository?
types