Как пройти собеседование по C#: топ вопросов и ответов
Собеседование на позицию C#/.NET разработчика — это проверка как теоретических знаний платформы, так и практических навыков написания кода. Работодатели хотят видеть не просто человека, который умеет писать код, а инженера, понимающего внутреннее устройство .NET, управление памятью, асинхронность и принципы SOLID.
В этой статье мы собрали топ-10 вопросов, которые чаще всего задают на собеседованиях по C#. Вы узнаете не только правильные ответы, но и подводные камни, на которые обращают внимание интервьюеры. Материал будет полезен как junior-разработчикам, так и специалистам middle-уровня.
1. Вопросы по основам ООП в C#
Без понимания объектно-ориентированного программирования собеседование по C# пройти невозможно. Интервьюеры проверяют не только определения, но и умение применять принципы на практике.
Какие принципы ООП реализованы в C#?
C# полностью поддерживает четыре столпа ООП:
- Инкапсуляция — скрытие внутренней реализации через модификаторы доступа (
private,protected,internal). - Наследование — класс может наследовать поведение от одного базового класса (одиночное наследование).
- Полиморфизм — возможность работать с разными типами через единый интерфейс (виртуальные методы, абстрактные классы).
- Абстракция — выделение существенных характеристик объекта и игнорирование несущественных (интерфейсы, абстрактные классы).
Пример вопроса: "Чем отличается абстрактный класс от интерфейса?"
Это классика. Ключевое отличие — интерфейс задает только контракт (сигнатуры методов без реализации), а абстрактный класс может содержать как реализацию, так и состояние (поля). В C# начиная с версии 8.0 интерфейсы могут иметь реализацию по умолчанию, но это не отменяет концептуальной разницы.
// Абстрактный класс
public abstract class Animal
{
public string Name { get; set; }
public abstract void MakeSound(); // без реализации
public void Sleep() => Console.WriteLine("Zzz..."); // с реализацией
}
// Интерфейс
public interface IMovable
{
void Move(); // только контракт
}
2. Управление памятью и Garbage Collector
Понимание работы GC — признак опытного разработчика. Вопросы на эту тему часто становятся фильтром между junior и middle.
Как работает сборщик мусора в .NET?
GC работает в managed heap и освобождает память от объектов, на которые больше нет ссылок. Он использует поколенческую модель (Generation 0, 1, 2):
- Gen 0 — короткоживущие объекты (очищается чаще всего).
- Gen 1 — буфер между Gen 0 и Gen 2.
- Gen 2 — долгоживущие объекты (очищается редко).
Пример вопроса: "Что такое финализация и IDisposable?"
Финализатор (~ClassName()) вызывается GC перед освобождением памяти, но время его вызова недетерминировано. Для детерминированной очистки неуправляемых ресурсов используют паттерн IDisposable:
public class ResourceHolder : IDisposable
{
private bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) { /* освобождаем managed ресурсы */ }
/* освобождаем unmanaged ресурсы */
_disposed = true;
}
~ResourceHolder() => Dispose(false);
}
3. Асинхронное программирование: async/await
Современное C#-собеседование редко обходится без вопросов про асинхронность. Это ключевая тема для создания отзывчивых приложений.
Чем отличается async/await от Thread?
async/await — это кооперативная многозадачность на основе Task Parallel Library (TPL). Поток не блокируется, а освобождается для других задач. В отличие от Thread, который является низкоуровневым объектом ОС и потребляет больше ресурсов.
Пример вопроса: "Что будет, если вызвать async метод без await?"
Метод выполнится синхронно до первого await, а затем вернет Task в состоянии Running. Исключения, выброшенные внутри async-метода, не будут перехвачены — они попадут в таск. Это частая причина багов.
public async Task<string> GetDataAsync()
{
await Task.Delay(1000);
throw new Exception("Ошибка");
}
// Опасный вызов без await
var task = GetDataAsync(); // исключение не упадет здесь
await task; // исключение упадет здесь
4. LINQ и работа с коллекциями
LINQ — это стандарт де-факто для работы с данными в C#. Без него сложно представить современный код.
Как работает отложенное выполнение (deferred execution) в LINQ?
Большинство LINQ-методов (например, Where, Select) не выполняются сразу — они возвращают IEnumerable<T>, который материализуется только при переборе (через foreach или вызов ToList()). Это позволяет оптимизировать запросы и избегать лишних вычислений.
Пример вопроса: "В чем разница между IEnumerable и IQueryable?"
IEnumerable работает в памяти (LINQ to Objects) — все данные загружаются, а затем фильтруются. IQueryable (LINQ to SQL/EF) строит Expression Tree, который транслируется в SQL и выполняется на стороне базы данных. Использование IEnumerable для запроса к БД приведет к загрузке всей таблицы в память.
// ПЛОХО: загрузит всех пользователей в память
IEnumerable<User> users = dbContext.Users.Where(u => u.Age > 18);
// ХОРОШО: выполнит SQL запрос на стороне БД