Установка и тулчейн .NET
Прежде чем писать код, нужно собрать окружение, которое будет работать одинаково на
вашей машине, у коллеги и на CI. В мире .NET это на удивление просто: почти всё, что
нужно, лежит внутри одного SDK, а dotnet CLI — единая точка входа для создания,
сборки, запуска, тестирования и публикации.
SDK vs Runtime
Различайте две вещи:
- .NET Runtime — то, что нужно, чтобы запускать уже собранное приложение.
- .NET SDK — включает runtime плюс компилятор,
dotnetCLI, шаблоны и MSBuild. Для разработки ставим именно SDK.
Скачать: dotnet.microsoft.com/download. На Linux/macOS удобно через пакетный менеджер или официальный скрипт. Проверяем:
# Версия установленного SDK
dotnet --version # напр. 9.0.100
# Полный список установленных SDK и runtime
dotnet --info
# Список установленных SDK
dotnet --list-sdks
global.json — фиксируем версию SDK
Чтобы команда и CI собирали проект одним и тем же SDK, кладём в корень репозитория
global.json. Это устраняет класс проблем «у меня собирается, у тебя нет».
{
"sdk": {
"version": "9.0.100",
"rollForward": "latestFeature"
}
}
rollForward: latestFeature означает «бери 9.0.1xx, но не прыгай на 9.0.2xx» —
разумный баланс между стабильностью и патчами.
dotnet CLI: команды, которые вы будете использовать каждый день
# Создать проекты из шаблонов
dotnet new console -o MyApp # консольное приложение
dotnet new webapi -o MyApi # ASP.NET Core Web API
dotnet new classlib -o MyApp.Core # библиотека классов
dotnet new xunit -o MyApp.Tests # проект тестов xUnit
dotnet new gitignore # правильный .gitignore для .NET
# Решение (solution) — контейнер для нескольких проектов
dotnet new sln -n MyApp
dotnet sln add MyApp/MyApp.csproj MyApp.Core/MyApp.Core.csproj
# Зависимости между проектами
dotnet add MyApp/MyApp.csproj reference MyApp.Core/MyApp.Core.csproj
# Пакеты NuGet
dotnet add package Serilog.AspNetCore
dotnet list package # что подключено
dotnet list package --outdated # что устарело
dotnet list package --vulnerable # что уязвимо (важно для безопасности!)
# Восстановление, сборка, запуск, тесты
dotnet restore
dotnet build -c Release
dotnet run --project MyApp
dotnet test
dotnet watch run # hot reload при изменении файлов
dotnet new list покажет все доступные шаблоны. Отдельно отметим dotnet watch —
он перезапускает или «горячо» перезагружает приложение при изменениях, что бесценно
при разработке веб-сервисов.
Анатомия проекта: .csproj и .sln
Проект описывается файлом .csproj — это XML на основе MSBuild. Современный формат
(SDK-style) минималистичен:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<!-- Nullable reference types: включаем ВСЕГДА -->
<Nullable>enable</Nullable>
<!-- Неявные using-и для распространённых namespace-ов -->
<ImplicitUsings>enable</ImplicitUsings>
<!-- Предупреждения = ошибки: держим код в чистоте -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Версия языка обычно выводится из TFM, но можно зафиксировать -->
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
</ItemGroup>
</Project>
Обратите внимание: файлы .cs не перечисляются вручную — SDK-style проект включает
все *.cs в папке автоматически. Это огромное упрощение по сравнению со старым форматом.
.sln — файл решения, группирующий проекты. Существует и новый упрощённый формат
.slnx (XML). В IDE вы открываете именно решение.
Central Package Management (CPM)
Как только проектов становится больше одного, версии пакетов начинают расползаться:
в одном проекте Serilog 8.0.0, в другом 8.0.1. Это рецепт конфликтов. Решение —
Central Package Management: версии задаются в одном месте.
Создаём в корне репозитория Directory.Packages.props:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="FluentValidation" Version="11.9.0" />
<PackageVersion Include="xunit" Version="2.9.2" />
</ItemGroup>
</Project>
Теперь в .csproj версия не указывается — только имя:
<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" />
</ItemGroup>
Единый источник правды по версиям — то, что нужно для воспроизводимых сборок и безопасности. Подробно: learn.microsoft.com/nuget/consume-packages/central-package-management.
Directory.Build.props — общие настройки на всё решение
Аналогично можно вынести общие свойства всех проектов в Directory.Build.props
в корне — например, Nullable, TreatWarningsAsErrors, TargetFramework, чтобы не
дублировать их в каждом .csproj.
<Project>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>
</Project>
Восстановление пакетов и безопасность цепочки поставок
dotnet restore тянет пакеты из настроенных источников (nuget.config). Для
воспроизводимости и защиты от подмены пакетов включайте lock-файлы и проверку:
# Зафиксировать точные версии транзитивных зависимостей
dotnet restore --use-lock-file # создаст packages.lock.json
В .csproj можно включить <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>.
На CI используйте dotnet restore --locked-mode — сборка упадёт, если lock-файл
разошёлся с реальностью. И регулярно гоняйте dotnet list package --vulnerable --include-transitive — это ваша первая линия обороны против известных CVE.
Форматтер и стиль: EditorConfig + dotnet format
Стиль кода в .NET задаётся файлом .editorconfig — его понимают и IDE, и
компилятор Roslyn, и CLI. Это единый механизм: правила именования, отступы, порядок
using-ов, а также включение/отключение конкретных анализаторов.
# .editorconfig в корне репозитория
root = true
[*.cs]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
# Стиль C#
csharp_style_namespace_declarations = file_scoped:warning
csharp_prefer_braces = true:warning
dotnet_style_require_accessibility_modifiers = always:warning
# Правило именования: приватные поля с _префиксом
dotnet_naming_rule.private_fields_underscore.severity = warning
dotnet_naming_rule.private_fields_underscore.symbols = private_fields
dotnet_naming_rule.private_fields_underscore.style = underscore_prefix
# Анализаторы: severity конкретных правил
dotnet_diagnostic.CA2007.severity = none # ConfigureAwait — не для приложений
Форматирование и проверка стиля из CLI:
dotnet format # исправить форматирование и стиль
dotnet format --verify-no-changes # на CI: упасть, если не отформатировано
Roslyn-анализаторы
Компилятор Roslyn имеет расширяемую систему анализаторов, которые ловят баги и
антипаттерны на этапе компиляции. Встроенные .NET-анализаторы (CAxxxx) включаются
через <AnalysisLevel> и <EnableNETAnalyzers>true</EnableNETAnalyzers>. Популярные
сторонние наборы:
- Meziantou.Analyzer, Roslynator — сотни дополнительных правил.
- SonarAnalyzer.CSharp — качество и security.
- StyleCop.Analyzers — жёсткий стиль (если команде нужен).
Подключаются как обычные NuGet-пакеты с PrivateAssets="all" (не утекают в
транзитивные зависимости).
IDE и LSP
- JetBrains Rider — мощнейшая IDE для .NET, кросс-платформенная.
- Visual Studio (Windows) — эталон, тяжёлый.
- VS Code + C# Dev Kit — лёгкий вариант, работает через LSP-сервер на базе Roslyn (github.com/dotnet/roslyn) и OmniSharp-наследника.
- Neovim/Emacs — через
csharp-lsили Roslyn LSP.
Любая из них даёт go-to-definition, рефакторинги, инлайн-подсказки анализаторов.
Рекомендуемый layout решения
Индустриальный стандарт — разделять исходники и тесты:
MyApp/
├── global.json
├── Directory.Build.props
├── Directory.Packages.props
├── .editorconfig
├── .gitignore
├── MyApp.slnx
├── src/
│ ├── MyApp.Api/ # точка входа (ASP.NET Core)
│ ├── MyApp.Application/ # бизнес-логика, use-cases
│ ├── MyApp.Domain/ # доменные модели
│ └── MyApp.Infrastructure/# EF Core, внешние сервисы
└── tests/
├── MyApp.UnitTests/
└── MyApp.IntegrationTests/
Такое разделение мы подробно разберём в статье про архитектуру. Пока запомните принцип: домен не зависит ни от чего, инфраструктура зависит от домена, а не наоборот.
Чеклист готового окружения
- Установлен .NET 8/9 SDK, проверен
dotnet --info. - Есть
global.jsonс зафиксированной версией SDK. -
NullableиTreatWarningsAsErrorsвключены (лучше вDirectory.Build.props). - Настроен
Directory.Packages.props(CPM). - Есть
.editorconfig,dotnet format --verify-no-changesв CI. - Включены анализаторы,
packages.lock.jsonпод контролем версий.
Что дальше
Основы языка — типы, nullable, записи, pattern matching, коллекции, LINQ и модель памяти. Фундамент, на котором строится всё остальное.