Библиотека шарписта | C#, F#, .NET, ASP.NET
21.7K subscribers
2.89K photos
45 videos
91 files
5.35K links
Все самое полезное для C#-разработчика в одном канале.

Наши курсы: https://clc.to/y3LDtw

По рекламе: @proglib_adv

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Download Telegram
😬 Microsoft показала ещё один кусочек будущего .NET

Вышел .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 и continue в C# 15. Выход из вложенных циклов без костылей

Знакомая боль. Есть вложенные циклы, и из внутреннего нужно прервать или продолжить внешний. Обычный 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🥱43
🤨 NativeAOT. Что значит скомпилировать .NET в нативный код заранее

NativeAOT снова на слуху, потому что в .NET 11 Preview 7 его сделали режимом по умолчанию в CLI ради быстрого старта. Разберём, что это вообще такое и чем отличается от привычной модели.

Обычно .NET работает так. Код на C# компилируется в промежуточный язык IL, а в нативные инструкции его переводит JIT уже во время запуска. Это гибко, но у первого запуска есть цена на разогрев, а вместе с приложением едет рантайм, который всё это исполняет.

NativeAOT переворачивает подход. Компиляция в машинный код происходит заранее, на этапе сборки. На выходе вы получаете самодостаточный нативный исполняемый файл, внутри которого нет ни IL, ни JIT. Приложению не нужен установленный рантайм, оно просто запускается.


🔴 Что это даёт. Старт почти мгновенный, ведь разогревать JIT нечего. Память на старте меньше, а размер развёртывания компактнее, потому что лишнее вырезается при сборке. Для консольных утилит, бессерверных функций и контейнеров, где важен быстрый холодный старт, это большой плюс.

🔴 Есть и цена. Раз всё решается на этапе сборки, в рантайме нельзя генерировать код и сильно ограничена рефлексия, ведь компилятор должен заранее видеть, что используется. Часть библиотек, завязанных на динамику, просто не поедет. Плюс сборка идёт под конкретную платформу, и кросс-компиляция не всегда проста.

NativeAOT отлично ложится на маленькие быстрые сервисы и утилиты, где ценны холодный старт и компактность. Для больших приложений с тяжёлой рефлексией и динамической загрузкой сначала проверьте совместимость. Включается это в файле проекта.


<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>


📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍4
🖍 NuGet сокращает зависимость от долгоживущих API-ключей

NuGet продолжает ужесточать безопасность публикации пакетов. Один из главных шагов — переход от постоянных API-ключей к короткоживущим credentials через Trusted Publishing.

Вместо хранения секрета в CI/CD workflow получает OIDC-токен, NuGet проверяет его и выдаёт временный API-ключ. Такой ключ действует около часа и затем становится недействительным.

Для CI/CD это означает простую вещь: вместо регулярной ручной ротации секретов можно настроить Trusted Publishing и вообще убрать долгоживущий NuGet API key из пайплайна.


Для обычных scoped API keys NuGet по-прежнему позволяет задавать срок действия самостоятельно.

💡 Если у вас есть автоматическая публикация пакетов, сейчас хороший момент проверить, не хранится ли долгоживущий NuGet API key прямо в CI/CD.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔4👍2🔥1😢1
🔖 Required + init: обязательные свойства без длинного конструктора

Раньше обязательные свойства часто приходилось передавать через конструктор или проверять уже во время выполнения.

🔵 В C# 11 появился required:


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 struct и in

Структуры передаются по значению — это знают все. Но копия может появиться и там, где её совсем не ожидаешь.
Представьте структуру с обычным, не 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
Visual Studio умеет превращать JSON в C#-классы за пару кликов

Не нужен онлайн-конвертер. Копируете JSON и в Visual Studio выбираете:


Edit → Paste Special → Paste JSON As Classes


IDE сама сгенерирует классы, включая вложенные объекты и массивы.

Особенно удобно, когда нужно быстро разобраться с ответом незнакомого API или сделать DTO для прототипа.

💡 А после генерации уже стоит привести классы в порядок: переименовать типы, добавить required/init, атрибуты сериализации и убрать лишнее.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🤩19🥱122😁1
🈶 Паттерны and, or и not. Условия, которые читаются как фраза

Проверки диапазонов в 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
👍156🌚3
🤨 ConfigureAwait(false). Почему его любят в библиотеках

При await .NET по умолчанию старается продолжить выполнение в захваченном контексте. Например, в UI-приложении это позволяет после await снова работать с UI-потоком. Но библиотеке обычно всё равно, в каком контексте продолжать работу. Поэтому там часто пишут:


var data = await httpClient
.GetStringAsync(url)
.ConfigureAwait(false);


ConfigureAwait(false) говорит: не нужно возвращать продолжение в исходный контекст.

Это особенно важно для библиотек: они не должны предполагать, что вызывающий код использует конкретный UI или другой SynchronizationContext.

Есть и классический сценарий с 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
🤩 F#: упрощённые иерархии интерфейсов с DIM

Раньше при работе с C#-интерфейсами, где базовый слот закрыт через дефолтную реализацию интерфейсов, F# всё равно требовал явно реализовать оба интерфейса.

Теперь достаточно реализовать только производный интерфейс.

🤩 Допустим, есть такие C#-интерфейсы:


public interface IA { int M(); }
public interface IB : IA {
new int M();
int IA.M() => this.M() + 100; // DIM покрывает слот IA.M
}


🤩 Раньше F# требовал реализовать и IA, и IB. Теперь достаточно IB:


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
😁42🔥2🥱2
📎 JsonSerializerOptions в каждом запросе это бомба замедленного действия

Вы оптимизировали базу данных. Ничего не изменилось. Оптимизировали сетевые вызовы. Всё ещё медленно.

Оказалось, приложение создаёт новый 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 сериализацию как горячую точку
— А вы не понимаете почему, если оптимизировали всё остальное


🤩 Создайте JsonSerializerOptions один раз при старте приложения и переиспользуйте везде:


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, и оригинал тоже меняется.

Правило трёх:

🤩 Records с init-only свойствами для value-like поведения
🤩 ImmutableList<T> из System.Collections.Immutable — настоящие неизменяемые коллекции
🤩 Struct только для маленьких типов

📍 Навигация: ВакансииЗадачиСобесы

🐸Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
🤩 EF Core: нормальный способ засевать базу

В 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 защищает его механизмом блокировки миграций.

🤩 А HasData теперь лучше использовать для статических и детерминированных данных — например, фиксированного справочника. Для данных, зависящих от состояния БД, внешних API или требующих вычислений, Microsoft рекомендует UseSeeding.

Если используете async-вариант, реализуйте и UseSeeding, и UseAsyncSeeding — EF tooling пока опирается на синхронный метод.

🔗 Источник

📍 Навигация: ВакансииЗадачиСобесы

🐸Библиотека шарписта

#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
7🔥1
😎 Три строки C# — и ваш AI-агент уже готов к хостингу в Azure

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-слой.

🔗 Источник: From dotnet run to Foundry Hosted Agent in 3 lines of C#

📍 Навигация: ВакансииЗадачиСобесы

🐸Библиотека шарписта

#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🤩5🥱1