Помните те времена, когда каждый метод начинался с целой простыни проверок входных параметров? Копипаста if (string.IsNullOrEmpty(...)) была ежедневной рутиной:
public void ProcessUser(string name, string email, int age)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Value cannot be null or empty.", nameof(name));
if (string.IsNullOrEmpty(email))
throw new ArgumentException("Value cannot be null or empty.", nameof(email));
if (age < 0)
throw new ArgumentOutOfRangeException(nameof(age), "Value must be non-negative.");
// Наконец-то бизнес-логика!
}
Код становился шумным, а реальная логика терялась в океане проверок. Каждый разработчик писал по-своему, сообщения об ошибках отличались, а про опечатки в nameof() вообще молчим.
Теперь всё это превращается в лаконичные однострочники:
public void ProcessUser(string name, string email, int age)
{
ArgumentException.ThrowIfNullOrEmpty(name);
ArgumentException.ThrowIfNullOrEmpty(email);
ArgumentOutOfRangeException.ThrowIfNegative(age);
}
Стандартная библиотека предлагает методы на все случаи жизни:
// Проверки на null
ArgumentNullException.ThrowIfNull(user);
// Числовые диапазоны
ArgumentOutOfRangeException.ThrowIfNegative(temperature);
ArgumentOutOfRangeException.ThrowIfZero(divisor);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);
// Сравнения
ArgumentOutOfRangeException.ThrowIfGreaterThan(progress, 100);
ArgumentOutOfRangeException.ThrowIfLessThan(quantity, 1);
ArgumentOutOfRangeException.ThrowIfEqual(status, Status.Invalid);
ArgumentOutOfRangeException.ThrowIfNotEqual(version, expectedVersion);
Эти методы — не просто синтаксический сахар. Они воплощают принцип fail-fast: обнаруживай проблемы немедленно, не позволяй невалидным данным распространяться по системе.
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24❤7👾4❤🔥1
Вышел .NET 11 Preview 7. Апдейт затронул почти весь стек: C#, Runtime, SDK, ASP
.NET Core, EF Core, MAUI, F# и Windows Forms.Главное изменение это перевод NativeAOT в режим по умолчанию в CLI ради заметно более быстрого старта приложений.
Из языка подъехали фичи C# 15, среди них паттерны union и помеченные break и continue для управления вложенными циклами.
В библиотеки добавили десятичные типы по стандарту IEEE 754 и шифрование паролем для ZIP.
Стабильный .NET 11 ожидается в ноябре, а пока Preview 7 можно использовать как полигон: проверить свои проекты на новом SDK и заранее найти проблемы с совместимостью.
👍 — тестирую превью
🔥 — до релиза даже не трогаю
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥45👍6🤩2
Знакомая боль. Есть вложенные циклы, и из внутреннего нужно прервать или продолжить внешний. Обычный
break выходит только из ближайшего цикла, поэтому приходилось заводить булев флаг и проверять его на каждом уровне, либо прыгать через goto. И то и другое замусоривает логику.В C# 15 у циклов появились метки, а
break и continue научились указывать, какой именно цикл они имеют в виду. Метка ставится прямо на нужный цикл.
outer: for (int row = 0; row < grid.Height; row++)
{
for (int col = 0; col < grid.Width; col++)
{
if (grid[row, col].IsBlocked)
continue outer;
if (grid[row, col].IsGoal)
break outer;
}
}
continue outer переходит к следующей итерации внешнего цикла, а break outer полностью выходит из него, минуя остаток внутреннего. Никаких флагов и подсчёта уровней.Пара нюансов.
break можно нацелить и на цикл, и на switch, а continue только на цикл, ведь продолжать switch нечего. И метка привязывается именно к тому циклу, перед которым стоит, поэтому двусмысленности, как с goto, здесь нет.#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥36🥱4❤3
NativeAOT снова на слуху, потому что в .NET 11 Preview 7 его сделали режимом по умолчанию в CLI ради быстрого старта. Разберём, что это вообще такое и чем отличается от привычной модели.
Обычно .NET работает так. Код на C# компилируется в промежуточный язык IL, а в нативные инструкции его переводит JIT уже во время запуска. Это гибко, но у первого запуска есть цена на разогрев, а вместе с приложением едет рантайм, который всё это исполняет.
NativeAOT переворачивает подход. Компиляция в машинный код происходит заранее, на этапе сборки. На выходе вы получаете самодостаточный нативный исполняемый файл, внутри которого нет ни IL, ни JIT. Приложению не нужен установленный рантайм, оно просто запускается.
NativeAOT отлично ложится на маленькие быстрые сервисы и утилиты, где ценны холодный старт и компактность. Для больших приложений с тяжёлой рефлексией и динамической загрузкой сначала проверьте совместимость. Включается это в файле проекта.
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍4
NuGet продолжает ужесточать безопасность публикации пакетов. Один из главных шагов — переход от постоянных API-ключей к короткоживущим credentials через Trusted Publishing.
Вместо хранения секрета в CI/CD workflow получает OIDC-токен, NuGet проверяет его и выдаёт временный API-ключ. Такой ключ действует около часа и затем становится недействительным.
Для CI/CD это означает простую вещь: вместо регулярной ручной ротации секретов можно настроить Trusted Publishing и вообще убрать долгоживущий NuGet API key из пайплайна.
Для обычных scoped API keys NuGet по-прежнему позволяет задавать срок действия самостоятельно.
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔4👍2🔥1😢1
Раньше обязательные свойства часто приходилось передавать через конструктор или проверять уже во время выполнения.
public class User
{
public required string Email { get; init; }
public required string Name { get; init; }
public int Age { get; init; }
}
var user = new User
{
Name = "Ada"
}; // ошибка компиляции
var user = new User
{
Email = "ada@x.io",
Name = "Ada"
};
init дополняет эту конструкцию: свойство можно задать при создании объекта, но нельзя изменить обычным присваиванием после инициализации.
Если обязательные свойства заполняются внутри конструктора, можно использовать [SetsRequiredMembers], чтобы сообщить об этом компилятору.
required отвечает за обязательность при создании, а init — за запрет последующего присваивания 💡
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥4👏1
Структуры передаются по значению — это знают все. Но копия может появиться и там, где её совсем не ожидаешь.
Представьте структуру с обычным, не readonly, методом. Если она находится в readonly-контексте, например в readonly поле или приходит через in, компилятору приходится защищать значение от изменения. При вызове такого метода он может создать защитную копию структуры.
public readonly struct Point
{
public int X { get; }
public int Y { get; }
public int Sum() => X + Y;
}
Можно пометить readonly и отдельные методы или свойства, если всю структуру сделать неизменяемой нельзя.
in при этом позволяет передавать большую структуру по ссылке без обычного копирования, а readonly-члены помогают избежать защитных копий при работе с такой ссылкой.
Если структура концептуально неизменяемая, readonly struct — хороший выбор по умолчанию. А для больших структур in может снизить стоимость передачи, но его эффект зависит от конкретного сценария
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4🔥2👍1
This media is not supported in your browser
VIEW IN TELEGRAM
Не нужен онлайн-конвертер. Копируете JSON и в Visual Studio выбираете:
Edit → Paste Special → Paste JSON As Classes
IDE сама сгенерирует классы, включая вложенные объекты и массивы.
Особенно удобно, когда нужно быстро разобраться с ответом незнакомого API или сделать DTO для прототипа.
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🤩19🥱12❤2😁1
Проверки диапазонов в C# легко превращаются в цепочки сравнений с && и ||. Паттерны позволяют записать их компактнее.
С C# 9 условия можно комбинировать прямо внутри pattern matching:
string Grade(int score) => score switch
{
< 0 or > 100 => "некорректно",
>= 90 => "отлично",
>= 60 and < 90 => "нормально",
_ => "плохо",
};
not особенно удобен для проверок:
if (value is not null)
Process(value);
if (obj is not string)
return;
Паттерны можно комбинировать и с проверкой свойств:
bool IsAdultAdmin(User u) =>
u is { Age: >= 18, Role: "admin" };
Вместо нескольких if получаем одно выражение, где условие читается почти как обычная фраза.
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤6🌚3
При await .NET по умолчанию старается продолжить выполнение в захваченном контексте. Например, в UI-приложении это позволяет после await снова работать с UI-потоком. Но библиотеке обычно всё равно, в каком контексте продолжать работу. Поэтому там часто пишут:
var data = await httpClient
.GetStringAsync(url)
.ConfigureAwait(false);
ConfigureAwait(false) говорит: не нужно возвращать продолжение в исходный контекст.
Есть и классический сценарий с deadlock:
var result = GetDataAsync().Result;
Если GetDataAsync() после await пытается вернуться в занятый контекст, продолжение не может выполниться, а .Result не может завершиться.
В ASP.NET Core собственного SynchronizationContext для запросов нет, поэтому классическая проблема с .Result и захватом контекста там не возникает по этой причине.
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
💯6👍4
Раньше при работе с C#-интерфейсами, где базовый слот закрыт через дефолтную реализацию интерфейсов, F# всё равно требовал явно реализовать оба интерфейса.
Теперь достаточно реализовать только производный интерфейс.
public interface IA { int M(); }
public interface IB : IA {
new int M();
int IA.M() => this.M() + 100; // DIM покрывает слот IA.M
}
type C() =
interface IB with member _.M() = 42
(C() 😆 IB).M() // 42
(C() 😆 IA).M() // 142 — DIM перенаправляет: this.M() + 100
Улучшение работает не только для простых случаев. Поддерживается ромбовидное наследование, дженерик-интерфейсы, свойства, события, структуры и объектные выражения.
Для включения нужен флаг --langversion:preview.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
😁4❤2🔥2🥱2
Вы оптимизировали базу данных. Ничего не изменилось. Оптимизировали сетевые вызовы. Всё ещё медленно.
Оказалось, приложение создаёт новый JsonSerializerOptions в каждом запросе. Это уничтожает встроенный кеш метаданных System.Text.Json и превращает JSON сериализацию в дорогую операцию, которая повторяется сотни раз в секунду под нагрузкой.
JsonSerializerOptions это не просто настройки. Это место, где System.Text.Json хранит кэшированные метаданные о том, как сериализовать и десериализовать типы.
Каждый новый экземпляр JsonSerializerOptions начинает с пустого кеша. System.Text.Json должна заново анализировать тип, строить информацию о сериализации, кешировать её. Потом запрос закончился и всё выбросилось.
Следующий запрос приходит. Новый экземпляр. Пустой кеш снова. Всё сначала.
Microsoft так серьёзно относится к этому, что добавили анализатор CA1869, который явно предупреждает: не создавайте JsonSerializerOptions локально в горячих путях.
string ToJson(object value)
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
WriteIndented = false
};
return JsonSerializer.Serialize(value, options);
}
Под нагрузкой это выглядит как:
— CPU растёт без видимых причин
— Задержка становится нестабильной, p99 скачет
— Профилер показывает JSON сериализацию как горячую точку
— А вы не понимаете почему, если оптимизировали всё остальное
public static class JsonDefaults
{
public static readonly JsonSerializerOptions Web = new(JsonSerializerDefaults.Web)
{
WriteIndented = false,
Converters = { new JsonStringEnumConverter() }
};
}
Используем кеш, никаких затрат
return JsonSerializer.Serialize(payload, JsonDefaults.Web);
Одна переменная, кеш остаётся тёплым, производительность стабильной.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25🤔2
Часто новички в функциональном программировании думают, что сделали иммутабельный код. Берут список, вызывают ToList() или Select() и радуются новому списку. Но в C# это ловушка.
Проблема в ссылочных типах. var copy = original.ToList() создаёт новый List, но все элементы внутри — те же самые объекты. Меняете свойство через copy, и оригинал тоже меняется.
Правило трёх:
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
В EF Core 9 появились UseSeeding() и UseAsyncSeeding(), а в актуальном EF Core 10 это уже рекомендуемый Microsoft способ для обычного заполнения базы начальными данными.
optionsBuilder
.UseSeeding((context, _) =>
{
if (!context.Set<Role>().Any())
{
context.Set<Role>().AddRange(
new Role { Name = "Admin" },
new Role { Name = "User" });
context.SaveChanges();
}
});
Плюс в том, что seed выполняется вместе с Migrate и dotnet ef database update, а EF защищает его механизмом блокировки миграций.
Если используете async-вариант, реализуйте и UseSeeding, и UseAsyncSeeding — EF tooling пока опирается на синхронный метод.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥1
Microsoft показала интеграцию .NET с Foundry Hosted Agents: существующий агент на Microsoft Agent Framework можно превратить в hosted agent практически без изменения логики.
dotnet add package Microsoft.Agents.AI.Foundry.Hosting --prerelease
var builder = AgentHost.CreateBuilder(args);
builder.Services.AddFoundryResponses(agent);
builder.RegisterProtocol(
"responses",
endpoints => endpoints.MapFoundryResponses());
azd provision
azd deploy
Foundry берёт на себя инфраструктуру, масштабирование, состояние сессий, identity, endpoint и observability. При этом сам AIAgent практически не меняется — вы просто добавляете вокруг него hosting-слой.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🤩5🥱1