Skip to content

Вопросы на собеседовании: .NET-разработчик

100 реальных вопросов с образцовыми ответами и пояснениями для уровня Junior .NET-разработчик.

Смотреть пример резюме: .NET-разработчик

Тренировка флешкарточками

Интервальное повторение · Hunter Pass

Вопросы

runtime

C# является языком программирования, .NET является платформой и runtime-экосистемой, а CLR является средой выполнения управляемого .NET-кода.

  • Компилятор C# обычно создаёт Common Intermediate Language и метаданные сборки.
  • CLR загружает сборки, проверяет и компилирует методы для выполнения и предоставляет runtime-сервисы.
  • .NET также включает базовые библиотеки, SDK, инструменты сборки и фреймворки приложений.

Зачем это спрашивают: Интервьюер проверяет, различаете ли вы язык, платформу и её среду выполнения.

JIT-компилятор переводит промежуточный язык метода в нативный машинный код, когда метод требуется во время выполнения.

  • Сгенерированный код предназначен для текущей операционной системы и архитектуры процессора.
  • Метод может быть скомпилирован при первом вызове и затем переиспользоваться в течение жизни процесса.
  • .NET также поддерживает ahead-of-time варианты, но JIT остаётся основной моделью выполнения CLR.

Зачем это спрашивают: Интервьюер оценивает базовое понимание того, как скомпилированный C# доходит до нативного выполнения.

types

Переменная value type содержит само значение, а переменная reference type содержит ссылку на объект.

  • Присваивание value type обычно копирует содержащееся значение.
  • Присваивание reference type копирует ссылку, поэтому обе переменные могут указывать на один объект.
  • Structs, enums и встроенные числовые типы являются value types, а classes, arrays и delegates являются reference types.

Зачем это спрашивают: Сильный ответ объясняет семантику присваивания, а не сводит различие только к стеку и куче.

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.

Зачем это спрашивают: Интервьюер оценивает, связываете ли вы категорию типа с копированием, наследованием и назначением модели.

types

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 вывод от динамической типизации.

nullabletypes

Nullable value type T? может представлять либо значение T, либо отсутствие значения.

  • int? является сокращением для Nullable<int>.
  • HasValue и Value открывают состояние, хотя Value выбрасывает исключение при отсутствии значения.
  • Оператор ?? задаёт fallback без ручной проверки HasValue.

Зачем это спрашивают: Интервьюер оценивает понимание того, как value types явно представляют отсутствие.

nullabletypes

Nullable reference types являются compile-time аннотациями и анализом, различающим ссылки с намерением non-null и ссылки, которым разрешён null.

  • Включённый nullable-контекст трактует string как намерение non-null, а string? разрешает null.
  • Компилятор выдаёт предупреждения, если код может разыменовать null или нарушить эти аннотации.
  • Возможность не добавляет runtime-проверки и не создаёт другой CLR-тип ссылки.

Зачем это спрашивают: Сильный ответ объясняет и пользу статической безопасности, и отсутствие новой runtime-семантики.

fundamentals

Операторы ?., ?? и ! выражают разные части обработки 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