Вступление: Легенда о «мёртвом» C#
Вы наверняка слышали, что C# — это язык для «корпоратов», где всё скучно, медленно и покрыто слоем энтерпрайз-бюрократии. Но правда в том, что C# разработчик сегодня — это универсальный солдат IT. Он пишет быстрые микросервисы на ASP.NET Core, оптимизирует запросы к БД через Entity Framework, а иногда даже встраивает машинное обучение с ML.NET.
В этой статье я не буду рассказывать абстрактные теории. Мы пройдём типичный рабочий день C# программиста в реальной компании: от утреннего кофе до пуша кода в прод. Вы увидите примеры кода, инструменты и лайфхаки, которые сэкономят часы работы.
08:30 — Утро: Кофе, почта и доска задач
День начинается не с кода, а с контекста. Первым делом я открываю Slack и Jira. В типичной команде есть утренний стендап (daily stand-up). На нём мы отвечаем на три вопроса:
- Что сделано вчера?
- Что буду делать сегодня?
- Есть ли блокеры?
После стендапа — беглый просмотр пул-реквестов коллег. Код-ревью на C# — это не просто «галочка». Здесь важно проверить, нет ли утечки памяти (особенно с IDisposable) и правильно ли используется async/await.
Пример из жизни: код-ревью асинхронного метода
// Плохо: блокирующий вызов внутри async метода
public async Task<User> GetUserAsync(int id)
{
var user = _context.Users.Find(id); // .Find — синхронный!
return await Task.FromResult(user); // Бесполезная обёртка
}
// Хорошо: полностью асинхронно
public async Task<User> GetUserAsync(int id)
{
return await _context.Users.FindAsync(id); // Реальная асинхронность
}
Такая мелочь может убить производительность под нагрузкой. Поэтому C# программист обязан следить за асинхронностью на каждом шагу.
10:00 — Работа с задачей: Пишем новый API endpoint
Сегодня в бэклоге — задача «Добавить фильтрацию товаров по цене». Это типичная история для .NET разработчика. Я работаю в Visual Studio 2022 с ReSharper (да, он тормозит, но без него как без рук).
Шаг 1: Модель и DTO
Сначала определяем модель данных. Допустим, у нас есть Entity Framework Core и база PostgreSQL.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
public class ProductFilterDto
{
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public string? Category { get; set; }
}
Шаг 2: Репозиторий с IQueryable
Главный трюк — не тащить все данные в память, а использовать IQueryable. Это позволяет сформировать SQL-запрос на стороне базы.
public async Task<List<Product>> GetFilteredProductsAsync(ProductFilterDto filter)
{
var query = _context.Products.AsQueryable();
if (filter.MinPrice.HasValue)
query = query.Where(p => p.Price >= filter.MinPrice.Value);
if (filter.MaxPrice.HasValue)
query = query.Where(p => p.Price <= filter.MaxPrice.Value);
if (!string.IsNullOrEmpty(filter.Category))
query = query.Where(p => p.Category == filter.Category);
return await query.ToListAsync();
}
Обратите внимание: мы не вызываем .ToList() до самого конца. Это базовый, но критически важный паттерн для любого C# разработчика.
Шаг 3: Контроллер ASP.NET Core
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _repo;
public ProductsController(IProductRepository repo) => _repo = repo;
[HttpGet("filter")]
public async Task<IActionResult> GetFiltered([FromQuery] ProductFilterDto filter)
{
var products = await _repo.GetFilteredProductsAsync(filter);
return Ok(products);
}
}
Весь процесс занял около 40 минут. Но рабочий день программиста не состоит только из написания кода.
11:30 — Мид-дей: Баги, логи и дебаг
Прилетает баг-репорт: «При загрузке файла больше 10 MB падает с ошибкой 413». Это классика для C# бэкенд разработчика.
Первым делом я смотрю логи в Serilog (или Application Insights). Нахожу ошибку:
System.IO.InvalidDataException: Multipart body length limit 10485760 exceeded.
Решение: увеличить лимит в Program.cs:
builder.Services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 50 * 1024 * 1024; // 50 MB
});
Но просто увеличить лимит — плохая практика. Нужно добавить проверку размера на клиенте и в middleware. Для этого пишем кастомный атрибут:
[AttributeUsage(AttributeTargets.Method)]
public class RequestSizeLimitAttribute : Attribute
{
public long SizeLimit { get; }
public RequestSizeLimitAttribute(long sizeInBytes) => SizeLimit = sizeInBytes;
}
Исправление бага заняло 20 минут. Ещё 10 — на написание юнит-теста (xUnit + Moq).
13:00 — Обед и рефлексия
Обед — это святое. В перерыве я читаю Stack Overflow или смотрю новые фичи C# 12. Например, primary constructors:
public class ProductService(IProductRepository repo) : IProductService
{
// repo уже доступен без отдельного поля
public async Task<Product> GetByIdAsync(int id) => await repo.GetByIdAsync(id);
}
Это упрощает код и уменьшает бойлерплейт. C# разработчик должен следить за обновлениями языка, чтобы не писать как в 2015 году.
14:30 — Вторая половина дня: Интеграция и тесты
После обеда — работа с внешним API. Нужно интегрировать сервис доставки. Использую HttpClientFactory (никогда не создавайте новый HttpClient вручную — это утечка сокетов).
Пример: типичная интеграция
public class DeliveryService : IDeliveryService
{
private readonly HttpClient _httpClient;
public DeliveryService(IHttpClientFactory factory)
{
_httpClient = factory.CreateClient("DeliveryApi");
}