Сеньорские Приёмы в C#
Некоторые функции C# настолько фундаментальны для чистой, современной разработки .NET, что их игнорирование выдаёт неопытность. Это не яркие новые игрушки, а практичные инструменты, которые мгновенно отличают код начинающего разработчика от отточенного кода опытного.
1. Раннее выявление ошибок внедрения зависимостей
Одна из самых серьёзных ошибок в приложениях .NET — это когда сервис успешно разрешается при запуске, но позже падает из-за недопустимых значений времени жизни или отсутствующих зависимостей. Этого можно избежать.
Код начинающего:
builder.Services.AddScoped<IMyService, MyService>();
// Нет настройки валидации
Вы не узнаете, что что-то неправильно настроено до того, как это не отвалится при обработке запроса.
Код опытного:
var builder = Program.CreateHostBuilder(args);
builder.Services.AddScoped<IMyService, MyService>();
builder.Host.UseDefaultServiceProvider(
(context, options) =>
{
options.ValidateOnBuild = true;
options.ValidateScopes = true;
});
Эти две строки гарантируют обнаружение захвата зависимостей при запуске приложения, а не при его работе. ValidateScopes обнаруживает недопустимые времена жизни (например, внедрение scoped-сервиса в синглтон), а ValidateOnBuild заставляет контейнер попытаться создать сервисы при построении контейнера. Если что-то сломается, вы узнаете об этом ещё до запуска приложения.
2. Понимание и создание областей действия сервисов
Многие разработчики помещают все сервисы в Transient, не понимая, что это значит. Время жизни — это не просто шаблонный код; это модель памяти вашего приложения.
Код начинающего:
builder.Services.AddTransient<UserSession>();
Ни контекста, ни объяснения, ни обдумывания, когда объект должен жить.
Ход мыслей опытного:
- Transient – новый экземпляр каждый раз,
- Scoped – один экземпляр на запрос/скоуп DI,
- Singleton – один экземпляр на всё время жизни приложения.
Создавая свою область действия, вы можете вручную изолировать контексты:
using var scope = serviceProvider.CreateScope();
var scopedService = scope
.ServiceProvider
.GetRequiredService<IScopedThing>();
Опытный разработчик знает, когда ему нужна изоляция сервиса, например, для каждого пользователя, для каждого запроса или для каждого фонового задания.
3. Использование DateTimeOffset
Часовые пояса, летнее время и работа с UTC — вот где начинающие разработчики чаще всего терпят неудачу. Хуже всего то, что ваше приложение может работать… но потом перестаёт.
Ошибка начинающего:
var orderTime = DateTime.Now; // локальное время
Это сломается, как только код попадёт в другой часовой пояс (например, при размещении в облаке).
Подход опытного:
var orderTime = DateTimeOffset.Now;
DateTimeOffset включает в себя как временную метку, так и её смещение. Это идеально подходит для реальных сценариев, таких как журналы, транзакции и планирование. Храните в формате UTC, отображайте по местному времени.
4. Использование алиасов пространств имён и типов
Загромождённые пространства имён и глубоко вложенные типы затрудняют чтение и рефакторинг кода. Тем не менее, начинающие разработчики редко используют алиасы:
System.Collections.Generic.Dictionary<System.Tuple<string, int>, List<MyNamespace.Models.ComplexThing>> myMap;
Более чистый подход:
using ComplexMap = System.Collections.Generic.Dictionary<(string, int), List<ComplexThing>>;
Алиасы — это не просто про сокращение имён; они проясняют суть. Они особенно полезны при рефакторинге или замене внешних библиотек, поскольку вы меняете всего одну строку — алиас.
Источник: https://blog.stackademic.com/5-c-features-that-instantly-expose-junior-devs-b126030c132e