Как пройти собеседование по C#: топ вопросов и ответы
Собеседование на позицию C# / .NET разработчика — это не только проверка технических знаний, но и умение мыслить, объяснять сложные концепции простыми словами. В этой статье мы разобрали самые частые вопросы, которые задают на интервью, с примерами кода и пояснениями. Материал будет полезен как junior, так и middle разработчикам.
1. В чем разница между value types и reference types?
Это базовый, но важный вопрос. В C# все типы делятся на две категории:
- Value types (типы значений) — хранятся в стеке, копируются при присваивании. Примеры:
int,double,struct,enum. - Reference types (ссылочные типы) — хранятся в куче (heap), переменная хранит ссылку на объект. Примеры:
class,string,array,delegate.
Пример для собеседования:
int a = 10;
int b = a;
b = 20;
Console.WriteLine(a); // 10 — значение не изменилось
var obj1 = new MyClass { Value = 10 };
var obj2 = obj1;
obj2.Value = 20;
Console.WriteLine(obj1.Value); // 20 — ссылка та же
На собеседовании также часто спрашивают про boxing/unboxing — преобразование value type в object и обратно. Старайтесь избегать этого в коде, так как это снижает производительность.
2. Что такое делегаты, события и лямбда-выражения?
Делегаты — это типы, которые хранят ссылки на методы. События — это обертка над делегатами, используемая для реализации паттерна "издатель-подписчик".
Пример делегата:
public delegate void MyDelegate(string message);
public static void ShowMessage(string msg) => Console.WriteLine(msg);
MyDelegate del = ShowMessage;
del("Hello from delegate");
Пример события:
public class Button
{
public event EventHandler Click;
public void Press() => Click?.Invoke(this, EventArgs.Empty);
}
Лямбда-выражения — это короткая запись анонимных методов. Они активно используются в LINQ:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var even = numbers.Where(x => x % 2 == 0).ToList();
На собеседовании могут спросить: "Почему лучше использовать события, а не делегаты?" — ответ: события защищают список подписчиков от внешнего изменения (нельзя вызвать Invoke извне).
3. Как работает async/await и в чем отличие от многопоточности?
Это один из самых популярных вопросов. async/await — это синтаксический сахар для работы с асинхронными операциями. Он не создает новый поток, а эффективно использует существующий (поток UI или поток из пула).
Ключевые моменты:
async— модификатор метода, разрешающий использованиеawait.await— приостанавливает выполнение метода до завершения задачи, не блокируя поток.- Возвращаемый тип — обычно
TaskилиTask<T>.
Пример:
public async Task<string> GetDataAsync()
{
using var client = new HttpClient();
string result = await client.GetStringAsync("https://api.example.com");
return result;
}
Отличие от многопоточности: многопоточность (Thread, Task.Run) — это параллельное выполнение кода на нескольких ядрах CPU. Async/await — это асинхронность, которая освобождает поток для других задач во время ожидания (например, ответа от сервера).
Совет: на собеседовании приводите реальный пример — загрузка данных из сети или файла. Избегайте async void (кроме обработчиков событий) — это может привести к необработанным исключениям.
4. Что такое LINQ и как он работает под капотом?
LINQ (Language Integrated Query) — это набор методов расширения для работы с коллекциями. Он реализован через отложенное выполнение (deferred execution).
Пример запроса:
var result = products
.Where(p => p.Price > 100)
.OrderBy(p => p.Name)
.Select(p => new { p.Name, p.Price });
Как работает: каждый метод возвращает IEnumerable<T>, который перечисляется только при вызове ToList(), ToArray() или в цикле foreach. Это позволяет строить цепочки запросов без лишних затрат памяти.
На собеседовании могут спросить про разницу между IEnumerable и IQueryable:
IEnumerable— выполняется в памяти (LINQ to Objects).IQueryable— выполняется на стороне БД (LINQ to SQL, Entity Framework).
5. Как работает Garbage Collector (GC) и что такое поколения?
GC в .NET автоматически управляет памятью. Он работает в три поколения (gen 0, gen 1, gen 2):
- Gen 0 — короткоживущие объекты (очищается чаще всего).
- Gen 1 — буфер между gen 0 и gen 2.
- Gen 2 — долгоживущие объекты (очищается редко).
Ключевые понятия:
- Large Object Heap (LOH) — для объектов > 85 КБ. Сборка мусора на LOH может быть дорогой.
- Dispose / IDisposable — для освобождения неуправляемых ресурсов (файлы, сокеты).
- Finalization — деструкторы (финализаторы) вызываются перед сборкой, но замедляют GC.
Совет: используйте using для гарантированного вызова Dispose()