50 важных вопросов для собеседования по C#
Проходите собеседования с ИИ помощником HintsageВведение
Эта статья — подборка из 50 неочевидных и реально полезных вопросов по C# и .NET, которые часто всплывают на собеседованиях.
Ответы не ограничиваются формальными определениями: есть контекст, примеры кода и подсказки, как об этом говорить на интервью.
Вопросы для начинающих и уверенных мидлов
1. В чем разница между типами значений (value types) и ссылочными типами (reference types) в C#?
В C# все типы делятся на:
- Типы значений (value types):
int,double,bool,struct,enumи др. - Ссылочные типы (reference types):
class,string, массивы,delegate,record classи др.
Ключевые различия:
- Для value type переменная хранит само значение.
- Для reference type переменная хранит ссылку на объект в куче.
- При присваивании value type копируется значение, а reference type — ссылка.
- Value types обычно живут в стеке или внутри других объектов, reference types — в куче (heap).
struct Point {
public int X;
public int Y;
}
class Person {
public string Name { get; set; }
}
var p1 = new Point { X = 1, Y = 2 };
var p2 = p1; // копия значений
p2.X = 100;
// p1.X == 1, p2.X == 100
var person1 = new Person { Name = "Alice" };
var person2 = person1; // копия ссылки
person2.Name = "Bob";
// person1.Name == "Bob"
На собеседовании часто спрашивают, что произойдет при передаче struct / class в метод по значению и по ссылке.
2. Что такое boxing и unboxing в C# и чем они опасны?
object или интерфейсный тип).Unboxing — обратное преобразование объекта обратно в value type.
int x = 42;
object boxed = x; // boxing
int y = (int)boxed; // unboxing
Проблемы:
- Каждый boxing создаёт новый объект в куче → лишние аллокации → больше работа для GC.
- Частый boxing в горячих участках (циклы, коллекции типа
ArrayList) заметно бьёт по производительности.
List<int>, Dictionary<int,string>) вместо старых не generics-коллекций.3. Чем отличаются class и struct в C#?
Основное:
class— ссылочный тип.struct— тип значения.
Различия по поведению:
structкопируется целиком при присваивании и передаче в метод.classкопирует только ссылку на один и тот же объект.structне может наследоваться от других struct (только отValueType), но может реализовывать интерфейсы.classподдерживает наследование и полиморфизм.
struct Money {
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency) {
Amount = amount;
Currency = currency;
}
}
class Order {
public Money Total { get; set; }
}
Struct лучше использовать для маленьких, логически атомарных и часто создаваемых неизменяемых типов (например, координаты, деньги, диапазоны).
4. Что такое immutable-типы и зачем нужны неизменяемые объекты в C#?
Неизменяемый объект (immutable) — это объект, состояние которого после создания изменить нельзя.
Примеры:
stringSystem.DateTimeSystem.TimeSpanrecord(обычно делают неизменяемыми)
Плюсы immutable-объектов:
- Проще многопоточный код — нет гонок (race conditions) за состояние.
- Проще reasoning и отладка — объект либо один, либо другой, без "полусостояний".
- Удобно кэшировать и переиспользовать.
Пример неизменяемого record:
public record User(string Name, int Age);
var u1 = new User("Alice", 30);
var u2 = u1 with { Age = 31 }; // создаётся новый объект
На собеседовании можно упомянуть, что неизменяемость — важный элемент функционального стиля и безопасного параллельного кода.
5. Что такое record в C# и чем он отличается от class?
record (C# 9+) — это особый вид ссылочного типа с семантикой сравнения по значению по умолчанию.public record User(string Name, int Age);
var u1 = new User("Alice", 30);
var u2 = new User("Alice", 30);
Console.WriteLine(u1 == u2); // true — сравнение по значению
Для record по умолчанию генерируются:
- методы
EqualsиGetHashCodeпо всем свойствам; - операторы сравнения
==и!=; - метод
Deconstructдля удобной деконструкции в кортеж.
class по умолчанию сравнивается по ссылке (если не переопределить сравнение).6. Объясните IDisposable и паттерн using в C#.
IDisposable используется для типов, которые владеют неуправляемыми ресурсами:- файловые дескрипторы;
- сетевые сокеты;
- подключения к базе;
- нативные хендлы и т.д.
Dispose() должен освобождать такие ресурсы.using (var stream = File.OpenRead("data.txt"))
{
// работа со stream
} // здесь stream.Dispose() будет вызван автоматически
С C# 8 можно писать:
using var connection = new SqlConnection(connString);
// Dispose вызовется в конце области видимости
~ClassName()).7. Как работает сборщик мусора (GC) в .NET и что такое поколения?
GC в .NET — generational collector. Он делит объекты по "возрасту" на поколения:
- Generation 0 — самые молодые объекты; сборка происходит часто.
- Generation 1 — промежуточные.
- Generation 2 — долгоживущие объекты (кэш, синглтоны).
> 85k).Идея:
- Большинство объектов умирает быстро.
- Поэтому выгодно чаще собирать молодые поколения и реже трогать старые.
На собеседовании можно упомянуть:
- что частые маленькие аллокации нормальны для .NET;
- но большие объекты и "вечно живущие" кэши нужно контролировать, чтобы не загаживать Gen2 и LOH.
8. Что такое delegate в C# и чем он отличается от event?
delegate — тип, представляющий ссылку на метод (или несколько методов с мультикастом).public delegate int Operation(int x, int y);
public int Sum(int a, int b) => a + b;
Operation op = Sum;
int result = op(2, 3);
event — более ограниченная оболочка вокруг делегата, предназначенная для паттерна "издатель–подписчик".public class Button {
public event EventHandler? Click;
protected void OnClick() {
Click?.Invoke(this, EventArgs.Empty);
}
}
+= или отписаться -=. Непосредственный вызов события извне запрещён (инкапсуляция).9. Что такое лямбда-выражения и анонимные методы в C#?
Анонимный метод — безымянный метод, который можно присвоить делегату.
Func<int, int> square = delegate(int x) { return x * x; };
Лямбда-выражение — более компактный синтаксис для анонимного метода:
Func<int, int> square = x => x * x;
Лямбды:
- могут захватывать внешние переменные (closures);
- широко используются в LINQ, событиях, TPL, API ASP.NET.
10. Что такое LINQ и чем отличаются query syntax и method syntax?
LINQ (Language Integrated Query) — набор расширений и синтаксиса для декларативной работы с коллекциями, БД, XML и др.
Два вида записи:
- Query syntax (SQL-подобный):
var evens = from n in numbers
where n % 2 == 0
select n;
- Method syntax:
var evens = numbers.Where(n => n % 2 == 0);
Компилятор переводит query syntax в method syntax. Важно понимать отличие:
- LINQ to Objects (работа в памяти,
IEnumerable); - LINQ to Entities (EF Core,
IQueryable, выражения транслируются в SQL).
11. В чем разница между IEnumerable и IQueryable?
-
IEnumerable<T>— интерфейс для последовательного перебора в памяти.
Операции LINQ выполняются в приложении после получения данных. -
IQueryable<T>— надстройка надIEnumerable<T>с деревом выражения (Expression).
Операции могут быть переведены, например, в SQL на стороне БД.
IEnumerable<User> usersInMemory = list.Where(u => u.Age > 18);
IQueryable<User> usersQuery = dbContext.Users.Where(u => u.Age > 18);
// Where будет трансформирован в SQL
ToList() слишком рано и "затащить" всю таблицу в память.12. Что такое async/await в C# и как оно работает под капотом?
async/await — синтаксический сахар вокруг Task и state machine.public async Task<string> DownloadAsync(string url)
{
using var client = new HttpClient();
var response = await client.GetStringAsync(url);
return response;
}
Под капотом:
- Компилятор превращает метод в машину состояний.
await"разрезает" метод на части: до ожидания и продолжение после завершения задачи.- Поток не блокируется, управление возвращается вызывающему коду, а продолжение запланируется позже.
На собеседовании часто спрашивают:
- чем async отличается от многопоточности,
- почему нельзя мешать
awaitи.Result/.Wait().
13. В чем разница между Task, Task<T> и ValueTask?
Task— асинхронная операция без возвращаемого результата.Task<T>— асинхронная операция, возвращающаяT.ValueTask<T>— облегчённая обертка, которая может хранить либоT, либоTask<T>.
public Task DoAsync() => Task.CompletedTask;
public Task<int> GetAsync() => Task.FromResult(42);
public ValueTask<int> GetFastAsync()
{
if (/* уже есть результат */)
return new ValueTask<int>(42);
return new ValueTask<int>(SlowAsync());
}
ValueTask уменьшает аллокации, но сложнее в правильном использовании (нельзя многократно await-ить один и тот же ValueTask и т.п.). По умолчанию, если нет профилированной необходимости, лучше использовать Task.14. Чем асинхронность (async/await) отличается от параллелизма (Parallel) в C#?
- Асинхронность — про неблокирующее ожидание (IO-bound задачи).
- Параллелизм — про использование нескольких ядер (CPU-bound задачи).
// Параллельная CPU-bound обработка
Parallel.For(0, 100, i => DoHeavyWork(i));
// Асинхронный IO-bound
await DownloadAsync(url);
Parallel.For или любую операцию на async. Нужно понимать характер нагрузки.15. Что такое lock и какие есть альтернативы синхронизации в C#?
lock — синтаксический сахар вокруг Monitor.Enter/Exit. Обеспечивает взаимное исключение в критических секциях.private readonly object _sync = new();
public void Increment() {
lock (_sync) {
_counter++;
}
}
Альтернативы:
MonitorMutex,Semaphore,SemaphoreSlimReaderWriterLockSlimInterlockedдля атомарных операций- concurrent-коллекции (
ConcurrentDictionary,ConcurrentQueue) и более высокоуровневые абстракции.
16. Как работает IAsyncDisposable и await using?
IAsyncDisposable и DisposeAsync().public class AsyncResource : IAsyncDisposable
{
public async ValueTask DisposeAsync()
{
// асинхронное освобождение
await Task.Delay(100);
}
}
await using var resource = new AsyncResource();
// ...
await using гарантирует, что DisposeAsync будет вызван с await.17. Что такое dependency injection (DI) и как он реализован в .NET?
DI — паттерн, когда объект не создаёт свои зависимости сам, а получает их извне (через конструктор, свойства, параметры методов).
public interface ILogger {
void Log(string message);
}
public class ConsoleLogger : ILogger {
public void Log(string message) => Console.WriteLine(message);
}
public class Service {
private readonly ILogger _logger;
public Service(ILogger logger) {
_logger = logger;
}
}
В современной .NET (ASP.NET Core):
- есть встроенный контейнер DI (
IServiceCollection,IServiceProvider); - зависимости регистрируются как
Singleton,Scoped,Transient.
18. Что такое attribute (атрибут) в C# и как его использовать?
System.Attribute, который добавляет метаданные к коду. Читается через рефлексию.[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuditAttribute : Attribute {
public string Action { get; }
public AuditAttribute(string action) => Action = action;
}
[Audit("CreateUser")]
public class UserController { }
Атрибуты активно используются:
- в ASP.NET Core (атрибуты контроллеров/действий),
- в ORM (EF Core),
- в сериализации (System.Text.Json, Newtonsoft.Json),
- в валидации (DataAnnotations).
19. Как работает рефлексия (Reflection) в C# и когда её стоит избегать?
Reflection позволяет во время выполнения:
- узнавать список сборок, типов, методов, свойств;
- создавать экземпляры типов;
- вызывать методы динамически.
var type = typeof(User);
var props = type.GetProperties();
foreach (var prop in props) {
Console.WriteLine(prop.Name);
}
Минусы:
- медленнее обычного кода;
- ломает инкапсуляцию;
- сложнее анализировать и тестировать.
Если возможно, лучше использовать:
- generics;
- выражения (
Expression); - source generators;
- явный код вместо рефлексии.
20. Что такое dynamic в C# и чем он отличается от var?
var— вывод типа во время компиляции. Тип известен компилятору.dynamic— динамическая типизация. Проверки типов переносятся в runtime.
var x = 10; // x: int
dynamic y = 10; // y: dynamic
y = "hello"; // ок, тип меняется на runtime
dynamic удобно для:- взаимодействия с COM;
- работы с динамическими языками;
- когда тип известен только в runtime.
Но легко получить runtime-ошибку, поэтому использовать аккуратно.
21. Что такое nullable reference types и как они помогают бороться с NullReferenceException?
С C# 8 можно включить режим nullable reference types:
#nullable enable
string? maybeNull = GetOrNull();
if (maybeNull != null) {
Console.WriteLine(maybeNull.Length);
}
string notNull = "test";
notNull = null; // warning
Суть:
stringозначает "по контракту не null".string?означает "может быть null".- Компилятор подсвечивает потенциально опасные места.
NullReferenceException в проде сильно падает, если следовать подсказкам.22. Как работает pattern matching в C# (оператор is, switch expressions)?
Pattern matching позволяет:
- проверять тип и сразу делать cast;
- проверять значения;
- сопоставлять с образцом (property patterns, positional patterns).
object o = GetValue();
if (o is int i && i > 0) {
Console.WriteLine($"Positive int: {i}");
}
string DescribeShape(Shape s) => s switch {
Circle c => $"Circle with radius {c.R}",
Rectangle { Width: var w, Height: var h } => $"Rect {w}x{h}",
_ => "Unknown"
};
Современный C# (9/10/11) сильно расширил pattern matching, и это часто спрашивают как "признак" актуальных знаний.
23. Что такое extension methods и когда их стоит использовать?
Extension method — статический метод, который выглядит как метод экземпляра.
public static class StringExtensions {
public static bool IsNullOrEmpty(this string? value) =>
string.IsNullOrEmpty(value);
}
// Использование
if (name.IsNullOrEmpty()) {
// ...
}
Нюансы:
- объявляется в статическом классе;
- первый параметр помечается
thisперед типом; - хорошо подходят для удобных утилит и fluent API;
- можно "расширить" типы из BCL и сторонних библиотек.
Злоупотреблять не стоит — можно превратить код в "свалку" extension-методов.
24. В чем разница между interface и abstract class в C#?
Кратко:
interfaceописывает контракт (набор членов).abstract classможет содержать и контракт, и частичную реализацию + состояние.
Различия:
- класс может реализовывать несколько интерфейсов, но наследоваться только от одного класса;
- в абстрактном классе можно хранить поля, защищенные методы, конструкторы;
- интерфейсы в C# 8+ могут иметь default-реализации методов, но не могут хранить состояние.
Интерфейсы — для слабой связности и полиморфизма, абстрактные классы — для общей базовой реализации.
25. Как работают generics в C# и чем они отличаются от дженериков в Java?
Generics в C# — рефицированные (reified):
- информация о типе параметра сохраняется в runtime;
- можно использовать
typeof(T),default(T), проверять типы, делать разные реализации в зависимости от T.
public class Repository<T> where T : Entity {
public T? FindById(Guid id) { ... }
}
new T[10].26. Зачем нужны ограничения обобщений (where T : ...), какие они бывают?
Ограничения позволяют сузить допустимые типы:
where T : struct— только value types;where T : class— только ссылочные типы;where T : new()— нужен public конструктор без параметров;where T : SomeBaseClass— наследник указанного базового класса;where T : ISomeInterface— реализует интерфейс.
public T CreateInstance<T>() where T : new()
{
return new T();
}
Ограничения повышают безопасность типов и позволяют писать более общий, но при этом безопасный код.
27. Чем отличаются ref, out и in параметры в C#?
Все три — про передачу по ссылке, но с разными правилами:
ref— параметр должен быть инициализирован до вызова и может быть изменен внутри метода.out— не обязан быть инициализирован до вызова, метод обязан присвоить значение перед выходом.in— передача по ссылке только для чтения (C# 7.2+), полезно для больших struct.
void Calc(ref int x, out int y) {
x += 10;
y = x * 2;
}
28. Что такое value tuple (ValueTuple) и чем он лучше старого Tuple?
ValueTuple:- struct (меньше аллокаций, нет лишних объектов в куче);
- поддерживает ИМЕНА элементов:
(int Id, string Name); - интегрирован в язык (деконструкция, возврат нескольких значений).
(int Id, string Name) GetUser() => (1, "Alice");
var user = GetUser();
Console.WriteLine(user.Name);
Tuple — reference type, без нормальных имён свойств (Item1, Item2).29. Как работают индексы и диапазоны (Index, Range, операторы ^ и ..) в C#?
С C# 8 появились:
index[^1]— элемент с конца;array[start..end]— slice (подпоследовательность).
var arr = new[] { 1, 2, 3, 4, 5 };
int last = arr[^1]; // 5
var middle = arr[1..4]; // {2, 3, 4}
Index и Range.30. Что такое Span<T> и Memory<T> и зачем они нужны?
Span<T> — "окно" на непрерывный блок памяти:- может ссылаться на массив, stackalloc, неуправляемую память;
- не создает копий при нарезке подмассивов;
ref struct, поэтому не может жить в куче и в async.
Span<int> slice = array.AsSpan(1, 3);
Memory<T> — похожая концепция, но может храниться в куче и использоваться в асинхронном коде.Обычно их используют в high-performance коде, парсерах, обработке больших буферов.
31. Что такое async void и почему его обычно нужно избегать?
async void:- нельзя
await-ить; - исключения из него не попадают в обычную цепочку
Taskи обрабатываются иначе; - его трудно тестировать и комбинировать с другими async-операциями.
async void OnClick(object sender, EventArgs e) {
await DoSomethingAsync();
}
async Task.32. Как работает ConfigureAwait(false) и когда его использовать?
await пытается вернуться в исходный контекст синхронизации (UI, ASP.NET).await SomeAsync().ConfigureAwait(false);
ConfigureAwait(false) говорит:"Не нужно возвращаться в исходный контекст, продолжай выполнение на любом потоке из пула."
Это:
- уменьшает накладные расходы;
- помогает избежать deadlock-ов в некоторых сценариях (особенно в старом ASP.NET и UI-приложениях);
- в библиотечном коде обычно всегда ставится
ConfigureAwait(false).
33. Что такое CancellationToken и как правильно отменять асинхронные операции?
CancellationToken — механизм кооперативной отмены. Создается через CancellationTokenSource и передается во все операции, которые поддерживают отмену.public async Task DoWorkAsync(CancellationToken token)
{
while (!token.IsCancellationRequested) {
await StepAsync(token);
}
}
Правильная отмена:
- проверять
IsCancellationRequested; - бросать
OperationCanceledExceptionпри необходимости; - не игнорировать токен в глубине стека.
34. Что такое lock-free структуры данных и какие есть примитивы в .NET?
lock), используя атомарные операции (CAS).В .NET:
- класс
Interlocked(Increment,CompareExchange); ConcurrentDictionary,ConcurrentQueue,ConcurrentBag,ConcurrentStack;System.Threading.Channels.
Плюсы:
- лучше масштабируются под высокой нагрузкой;
- меньше contention на блокировках.
Минусы:
- сложнее имплементировать и понимать.
35. Что такое async streaming (IAsyncEnumerable<T>) и await foreach?
Позволяет асинхронно возвращать последовательность значений, не загружая всё сразу.
public async IAsyncEnumerable<int> GetNumbersAsync()
{
for (int i = 0; i < 10; i++) {
await Task.Delay(100);
yield return i;
}
}
await foreach (var n in GetNumbersAsync()) {
Console.WriteLine(n);
}
Это удобно для стриминга данных из БД/сети, логов, парсинга больших файлов.
36. Чем отличаются middleware и filters в ASP.NET Core?
-
Middleware — компоненты конвейера обработки HTTP-запросов на уровне всего приложения:
- логирование,
- аутентификация,
- CORS,
- обработка ошибок и т.д.
-
Filters — механизм уровня MVC/Web API:
AuthorizationFilter,ActionFilter,ExceptionFilter,ResultFilter.
Они оборачивают вызов конкретных контроллеров/экшенов, а не весь pipeline.
37. Что такое Source Generators в C# и зачем они нужны?
Source Generators — механизм Roslyn, который позволяет во время компиляции:
- анализировать ваш код;
- генерировать дополнительный C#-код.
Применение:
- генерация сериализаторов (
System.Text.Jsonsource generators); - мапперы (Mapster, частично AutoMapper);
- DI-обертки;
- уменьшение использования рефлексии в runtime.
Это даёт производительность и безопасность, т.к. всё вычисляется на этапе компиляции.
38. Как корректно реализовать Equals, GetHashCode и оператор == для класса?
Если вы хотите сравнение по значению:
- Реализуйте
IEquatable<T>. - Переопределите
Equals(object). - Переопределите
GetHashCode(). - (Опционально) перегрузите
==и!=.
public class Point : IEquatable<Point> {
public int X { get; }
public int Y { get; }
public bool Equals(Point? other) =>
other is not null && X == other.X && Y == other.Y;
public override bool Equals(object? obj) => Equals(obj as Point);
public override int GetHashCode() => HashCode.Combine(X, Y);
public static bool operator ==(Point? a, Point? b) =>
Equals(a, b);
public static bool operator !=(Point? a, Point? b) => !(a == b);
}
Важно соблюдать контракт: равные объекты должны иметь одинаковый hash code.
39. Что такое deadlock и как он может возникнуть с async/await?
Deadlock — ситуация, когда задачи/потоки вечно ждут друг друга.
Классический пример в C#:
// Потенциальный deadlock в UI/старом ASP.NET
var result = SomeAsync().Result; // или .Wait()
SomeAsync внутри пытается вернуться в тот же контекст синхронизации, который сейчас заблокирован .Result, получаем взаимную блокировку.Лечение:
- использовать
awaitдо самого верха; - не блокировать асинхронный код синхронно;
- в библиотеках использовать
ConfigureAwait(false).
40. Что такое "memory leak" в .NET, если есть GC?
GC удаляет неиспользуемые объекты, но не умеет угадывать, что объект вам "больше не нужен".
Если на объект есть хоть одна живая ссылка, он считается достижимым и не удаляется.
Типичные источники утечек:
- статические коллекции, куда складывают всё подряд и никогда не чистят;
- события, где подписчик не отписался;
- кэши без политики очистки.
Это не "классическая" утечка, как в C/C++, но по факту — память уходит и не возвращается.
41. Как правильно работать с событиями, чтобы избежать утечек памяти?
Проблема:
- есть долгоживущий объект (издатель события),
- и короткоживущий подписчик,
- подписчик подписался на событие и не отписался.
Издатель держит ссылку на делегат, а делегат — на подписчика, и GC не может удалить подписчика.
Решения:
- явно отписываться в
Dispose/Close/ финализаторе; - использовать слабые ссылки (WeakReference) / weak events (WPF);
- проектировать так, чтобы издатель не жил намного дольше подписчика.
42. В чем разница между Task.Run и Thread в C#?
Thread— "голый" поток ОС: дорого создавать, управлять, нет интеграции с TPL.Task.Run— планирует задачу в пул потоков (ThreadPool), интегрирован с async/await, обработкой исключений, отменой.
// Плохо: вручную плодить потоки
new Thread(() => DoWork()).Start();
// Лучше: использовать пул потоков
await Task.Run(() => DoWork());
Task/TPL, а не голые Thread.43. Что такое retry-логика с backoff и как её реализовать в C#?
Retry (повторная попытка) нужен для временных (transient) ошибок: сетевые проблемы, временная недоступность сервиса.
Простой пример c экспоненциальным backoff:
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
await CallExternalServiceAsync();
break;
}
catch (TransientException) {
if (attempt == maxAttempts) throw;
var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt));
await Task.Delay(delay);
}
}
На практике часто используются библиотеки типа Polly.
44. Что такое configuration и options pattern в ASP.NET Core?
Options pattern — типизированная работа с конфигурацией:
- Определяем класс настроек:
public class MyOptions {
public string ApiKey { get; set; } = "";
public int TimeoutSeconds { get; set; }
}
- Биндим его к секции в
appsettings.json:
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
- Внедряем через DI (
IOptions<MyOptions>,IOptionsSnapshot<MyOptions>,IOptionsMonitor<MyOptions>).
Configuration["Key"] строками.45. В чем разница между синхронным и асинхронным API в EF Core?
ToList(),FirstOrDefault(),SaveChanges()— синхронные, блокируют поток, пока БД отвечает.ToListAsync(),FirstOrDefaultAsync(),SaveChangesAsync()— асинхронные, не блокируют поток и позволяют серверу обслуживать больше запросов.
На backend-собесах часто спрашивают:
- почему асинхронные методы предпочтительнее на высоконагруженном API;
- почему
asyncне делает запрос быстрее, но позволяет масштабироваться лучше.
46. Что такое DTO и почему его часто используют с AutoMapper?
DTO (Data Transfer Object) — простой класс для переноса данных между слоями/сервисами:
- без логики,
- только данные и простые правила валидации.
Доменные модели часто содержат поведение, сложные связи, lazy-loading и т.п., и напрямую светить их наружу (в API) не всегда безопасно / удобно.
AutoMapper:
- позволяет автоматически маппить свойства доменной модели в DTO по соглашениям;
- уменьшает boilerplate-код копирования.
47. Как реализовать кастомный middleware в ASP.NET Core?
Invoke или InvokeAsync, принимающим HttpContext.public class LoggingMiddleware {
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context) {
Console.WriteLine($"Request: {context.Request.Path}");
await _next(context);
}
}
// Program.cs
app.UseMiddleware<LoggingMiddleware>();
UseLogging() для красивой регистрации.48. Что такое Open/Closed Principle (OCP) и как он проявляется в C#-коде?
OCP (из SOLID):
"Программные сущности должны быть открыты для расширения, но закрыты для модификации."
В C# это часто значит:
- выделение интерфейсов и абстракций;
- использование DI и паттернов (стратегия, декоратор, фабрики);
- добавление нового поведения через новые классы/компоненты, а не изменение старых.
switch по типу оплаты лучше иметь IPaymentStrategy и разные реализации.49. Какие типичные архитектурные вопросы задают после технических по C#?
После языка обычно идут вопросы:
- Как разделяете слои (Web/API, Application, Domain, Infrastructure)?
- Используете ли DDD, CQRS, event sourcing?
- Как подходите к логированию, метрикам, tracing?
- Как организуете solution: проекты, зависимости?
- Как тестируете: unit, integration, end-to-end?
Важно показать, что вы умеете применять C# и .NET в контексте архитектуры и командной разработки.
50. Как подготовиться к собеседованию по C# и .NET, опираясь на эти вопросы?
Практический чек-лист:
- Пройдитесь по всем 50 вопросам и попробуйте:
- объяснить вслух каждую тему простыми словами;
- написать маленький пример кода "по памяти";
- придумать, где вы сталкивались с этим в реальных проектах.
- Отдельно потренируйте:
- async/await, CancellationToken, ConfigureAwait;
- LINQ и отличия IEnumerable/IQueryable;
- DI, жизненные циклы сервисов;
- ASP.NET Core (middleware, filters, конфигурация);
- EF Core (миграции, трекинг, асинхронные запросы).
Если вы можете уверенно и спокойно объяснить все эти темы — на большинстве собеседований по C# вы будете чувствовать себя очень уверенно.
Заключение
Эти 50 вопросов закрывают не только синтаксис C#, но и важные части стека .NET: память, асинхронность, LINQ, generics, DI, ASP.NET Core, EF Core и архитектурные подходы.
Используйте их как чек-лист: дополняйте своими заметками, кодом, примерами из проектов — и превратите это в свой небольшой консект перед собеседованием.