Введение
Собеседование на позицию C# разработчика — это не просто проверка синтаксиса. Работодатели хотят видеть понимание платформы .NET, принципов работы CLR, умение писать производительный код и знание современных подходов (async, LINQ, DI).
В этой статье мы разберем топ вопросов по C# от А до Я — от базовых типов данных до сложных тем вроде сборки мусора и асинхронного программирования. Каждый вопрос сопровождается кратким ответом и примером кода, чтобы вы могли уверенно чувствовать себя на интервью.
1. Базовые вопросы: типы, классы и память
1.1. В чем разница между Value Type и Reference Type?
Value Type (int, struct, enum) хранятся в стеке и копируются по значению. Reference Type (class, string, array) хранятся в куче, переменная содержит ссылку на объект.
int a = 10;
int b = a; // b — копия, a и b независимы
class MyClass { public int X; }
MyClass obj1 = new MyClass { X = 5 };
MyClass obj2 = obj1; // obj2 ссылается на тот же объект
obj2.X = 10; // obj1.X тоже станет 10
1.2. Что такое Boxing и Unboxing?
Boxing — упаковка значимого типа в объект (в кучу). Unboxing — обратный процесс. Частое использование снижает производительность.
int number = 42;
object boxed = number; // Boxing
int unboxed = (int)boxed; // Unboxing
1.3. Расскажите про сборщик мусора (Garbage Collector).
GC в .NET автоматически освобождает память от объектов, на которые нет ссылок. Работает поколениями (Gen 0, 1, 2). Объекты больших размеров (>85 КБ) попадают в Large Object Heap (LOH).
Совет: Не вызывайте GC.Collect() вручную — это нарушает работу оптимизаций GC.
2. ООП и принципы проектирования
2.1. Объясните принципы SOLID.
- S — Single Responsibility: класс должен иметь одну причину для изменения.
- O — Open/Closed: открыт для расширения, закрыт для модификации.
- L — Liskov Substitution: наследники должны корректно заменять базовый класс.
- I — Interface Segregation: много маленьких интерфейсов лучше одного большого.
- D — Dependency Inversion: зависимости должны быть от абстракций, а не от конкретных классов.
2.2. Что такое Dependency Injection (DI)?
DI — паттерн, при котором зависимости передаются в класс извне (через конструктор, метод или свойство). В .NET Core встроенный DI-контейнер.
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine(message);
}
public class OrderService
{
private readonly ILogger _logger;
public OrderService(ILogger logger) // DI через конструктор
{
_logger = logger;
}
}
3. LINQ, исключения и строки
3.1. Как работает LINQ? Что такое отложенное выполнение?
LINQ (Language Integrated Query) позволяет писать запросы к коллекциям, базам данных (EF Core) и XML. Отложенное выполнение означает, что запрос выполняется только при переборе результата (например, через foreach или .ToList()).
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var query = numbers.Where(n => n > 2); // запрос не выполнен
numbers.Add(6); // добавляем после создания запроса
foreach (var n in query) // выполнение здесь
Console.WriteLine(n); // 3, 4, 5, 6
3.2. В чем разница между String и StringBuilder?
String — неизменяемый тип. Каждая операция конкатенации создает новый объект. StringBuilder использует изменяемый буфер и эффективен для множественных изменений строки.
// Плохо (много аллокаций)
string s = "";
for (int i = 0; i < 1000; i++)
s += i.ToString();
// Хорошо
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
sb.Append(i);
string result = sb.ToString();
3.3. Как обрабатывать исключения? Что такое finally?
Используйте try-catch-finally. Блок finally выполняется всегда, даже если было исключение или return. Подходит для освобождения ресурсов.
FileStream file = null;
try
{
file = File.OpenRead("data.txt");
// работа с файлом
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Файл не найден");
}
finally
{
file?.Close(); // гарантированно закроем файл
}
4. Продвинутые темы: async, делегаты, потоки
4.1. Как работает async/await? Что такое SynchronizationContext?
async/await — это синтаксический сахар над Task. Метод помечается async, а await приостанавливает выполнение метода до завершения асинхронной операции, не блокируя поток. SynchronizationContext восстанавливает контекст (например, UI-поток в WPF).
public async Task<string> GetDataAsync()
{
using (HttpClient client = new HttpClient())
{
string result = await client.GetStringAsync("https://api.example.com");
return result;
}
}
4.2. Что такое делегаты и события (delegate, event)?
Делегат — тип, который хранит ссылку на метод. Событие — это делегат с ограничениями (нельзя вызвать извне класса).
public delegate void Notify(string message);
public class Publisher
{
public event Notify OnNotify;
public void Raise()
{
OnNotify?.Invoke("Событие сработало!");
}
}
// Использование
Publisher pub = new Publisher();
pub.OnNotify += msg => Console.WriteLine(msg);
pub.Raise();
4.3. В чем разница между lock, Monitor и Semaphore?
- lock — простейший механизм синхронизации