Библиотека шарписта | C#, F#, .NET, ASP.NET
22.6K subscribers
2.41K photos
39 videos
85 files
4.6K links
Все самое полезное для C#-разработчика в одном канале.

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

Учиться у нас: https://proglib.io/w/b60af5a4

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

РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Download Telegram
⚙️ Разбор .NET rc1

Недавно мы делились новостью о выходе .NET 10 RC1. Теперь пришло время посмотреть глубже: что именно изменилось, какие улучшения можно опробовать уже сейчас.

Повышение производительности без усилий

• Улучшения в рантайме и JIT-компиляторе, благодаря которым уже существующий код работает быстрее при перекомпиляции, без изменения кода.

• «Deabstraction»: устранение накладных расходов за счёт оптимизации интерфейсов, лямбд, async, итераторов и др.

• Расширенный «escape analysis», позволяющее небольшие объекты размещать в стеке, а не на куче, что уменьшает нагрузку на garbage collector.

Улучшения рантайма и GC

• Поддержка AVX 10.2 инструкций — подготавливает платформу к современному железу.

• Оптимизация фонового GC, улучшение сжатия памяти и управления фрагментацией.

Безопасность и квантовая устойчивость

• Внедрение апи для пост-квантовой криптографии.

• Новый алгоритм цифровой подписи ML-DSA (основанный на решёточных структурах), стандартизированный NIST.

Новые возможности для разработчиков и приложений

ASP.NET Core: более детальная телеметрия/метрики по аутентификации, токенам и двухфакторке, что облегчает выявление атак типа «credential stuffing», брут форс и др.

• EF Core: поддержка векторного поиска и нативного JSON в SQL Server — полезно для AI и полуструктурированных данных.

• .NET MAUI: метрики на уровне UI-layout операций (Measure, Arrange) — помогает обнаружить ботлнеки интерфейса.

C# 14 — язык становится дружелюбнее и эффективнее

• Возможность использовать params не только с массивами, но и со Span для уменьшения аллокаций.

• Новый field-контекст в свойствах упрощает код, убирает явное объявление «backing field» — меньше шаблонов, меньше шума.

Язык и среда .NET движутся в сторону объединения удобства и производительности: абстракции остаются, но их накладные расходы всё больше уходят.

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2583🥰1
💡 Что скрывается под капотом IReadOnlyList

В .NET есть интерфейс IReadOnlyList<T>. На первый взгляд, он обещает нам неизменяемый список. Но это полуправда.

Определение интерфейса:
public interface IReadOnlyList<out T> : IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
{
T this[int index] { get; }
}


Интерфейс наследует IReadOnlyCollection, а тот, в свою очередь, наследует IEnumerable.
Таким образом, он даёт доступ: к элементам через индекс (this[int index]), к свойству Count, и к итерации через foreach.

⚠️ Важный нюанс: IReadOnlyList не делает коллекцию реально readonly.

Если у вас есть List<int> и вы приведёте его к IReadOnlyList<int>, изменять список по-прежнему можно — просто не через эту ссылку.

Пример:
var list = new List<int> { 1, 2, 3 };
IReadOnlyList<int> ro = list;

list.Add(4); // ro теперь "видит" 4


То есть IReadOnlyList — это контракт для чтения, а не гарантия неизменности.
Если нужна настоящая защита — используйте ImmutableArray или ReadOnlyCollection.

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
12❤‍🔥7🥱7👍1
💡 Domain Validation в .NET и DDD — просто о сложном

В .NET-проектах часто пишут валидацию прямо в контроллере или сервисе:
— проверили, что поле не пустое,
— число больше нуля,
— email подходит по формату.

Это правильно, но есть проблема — бизнес-правила остаются размазаны по коду.
В итоге можно случайно создать объект в некорректном состоянии (например, заказ без товаров).

В идеале должно быть два уровня валидации Application Validation И Domain Validation. В одном проверяются входные данные, а в другом строится защита сущностей от нарушения бизнес-правил.

Пример проверки на уровне приложения:
public record CreateOrderDto(string CustomerEmail, List<OrderItemDto> Items);

public class OrderApplicationValidator
{
public static void Validate(CreateOrderDto dto)
{
if (string.IsNullOrWhiteSpace(dto.CustomerEmail))
throw new ArgumentException("Email is required");

if (!dto.CustomerEmail.Contains("@"))
throw new ArgumentException("Email format is invalid");

if (dto.Items == null || dto.Items.Count == 0)
throw new ArgumentException("Order must contain at least one item");
}
}


Здесь мы проверяем только корректность ввода.

А вот внутри домена мы защищаем бизнес-правила:
public class OrderItem
{
public string ProductName { get; }
public int Quantity { get; }
public Money Price { get; }

private OrderItem(string productName, int quantity, Money price)
{
ProductName = productName;
Quantity = quantity;
Price = price;
}

public static OrderItem Create(string productName, int quantity, Money price)
{
if (string.IsNullOrWhiteSpace(productName))
throw new InvalidOperationException("Product name cannot be empty");

if (quantity <= 0)
throw new InvalidOperationException("Quantity must be greater than zero");

return new OrderItem(productName, quantity, price);
}

public Money GetTotal() => Money.Create(Quantity * Price.Amount);
}


Когда правила закреплены в домене, они становятся частью самой логики, а не зависимыми от внешних слоёв. Это гарантирует, что объект в принципе невозможно построить в некорректном виде.

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥81👍1
📀 Record-типы в C#

record в C# — это удобный способ описывать объекты данных без лишнего кода. Они сравниваются по значению, остаются неизменяемыми и отлично подходят для DTO или моделей в DDD.

Если раньше приходилось писать десятки строк с Equals, GetHashCode, ToString и конструкторами, то теперь всё это даёт одна строчка:
public record User(string Name, int Age);


Отличие от классов

Class сравнивается по ссылке. Два объекта с одинаковыми данными — разные сущности.

Record сравнивается по значению. Два объекта с одинаковыми полями — эквивалентны.

var u1 = new User("Alice", 25);
var u2 = new User("Alice", 25);

Console.WriteLine(u1 == u2); // true


Дополнительные фишки

Записи неизменяемы, но их удобно клонировать с изменением:
var u1 = new User("Alice", 25);
var u2 = u1 with { Age = 26 };

Console.WriteLine(u2); // User { Name = Alice, Age = 26 }


Можно наследовать record-тип:
public record User(string Name, int Age);
public record Admin(string Name, int Age, string Role) : User(Name, Age);

record — это синтаксический сахар, который сокращает код и делает модели данных чище и понятнее.

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍241
⚙️ Под капотом async/await

Когда вы пишете асинхронный метод вроде:
async Task<int> GetDataAsync()
{
var data = await FetchAsync();
return data.Length;
}


вы можете думать, что это «всего лишь ожидание задачи». Но на самом деле компилятор C# превращает этот метод в state machine — конечный автомат, который управляет переходами между состояниями выполнения.

State machine — это структура, которая:

• хранит текущее состояние выполнения программы, например, до await, после await или завершено,

• знает, в какое состояние перейти дальше, когда наступает внешнее событие например, завершение Task.

Проще говоря, компилятор разворачивает ваш линейный асинхронный код в набор состояний + переключатель между ними.

Что делает компилятор

При компиляции метода с await создаётся вспомогательный класс, реализующий интерфейс IAsyncStateMachine.

Если упростить, то наш пример превращается примерно в:
private struct GetDataAsyncStateMachine : IAsyncStateMachine
{
public int _state;
public AsyncTaskMethodBuilder<int> _builder;
private TaskAwaiter<string> _awaiter;

public void MoveNext()
{
try
{
if (_state == 0)
{
// после await
var result = _awaiter.GetResult();
_builder.SetResult(result.Length);
return;
}

var task = FetchAsync();
if (!task.IsCompleted)
{
_state = 0;
_awaiter = task.GetAwaiter();
_builder.AwaitUnsafeOnCompleted(ref _awaiter, ref this);
return;
}

_builder.SetResult(task.Result.Length);
}
catch (Exception ex)
{
_builder.SetException(ex);
}
}

public void SetStateMachine(IAsyncStateMachine stateMachine) { }
}


Другие примеры использования state machines в C#

• yield return — генерация итераторов IEnumerator

• foreach на async коллекциях — асинхронные итераторы IAsyncEnumerable

Каждый await или yield превращает метод в "автомат", который сам управляет своим ходом выполнения.

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🥱74
🚀 JetBrains .NET Days 2025

JetBrains снова собрала лучших инженеров и евангелистов .NET на своей онлайн-конференции .NET Days. Два дня плотных докладов — от чистой архитектуры и распределённых систем до GenAI и F#.

1️⃣ Самое интересное за первый день:

• Чистая архитектура с ASP.NET Core

Как выстроить проект так, чтобы код был читаемым, тестируемым и легко рефакторился. Разделение слоёв и зависимостей без боли.

• Nullability в C#: включаем защиту от NullReferenceException

Аннотации и статический анализ помогают избавиться от NullReferenceException и внедрить null safety даже в старый код.

• Как выбрать систему обмена сообщениями

Сравнение AWS SQS, RabbitMQ и Azure Service Bus — плюсы, минусы и типичные ошибки при выборе очередей сообщений.

• TDD на фронтенде с Blazor

Как применять TDD на фронтенде с Blazor и bUnit. Быстрая обратная связь и уверенность в каждом изменении.

2️⃣ Темы за второй день:

• Версионирование событийных систем

Безопасные приёмы эволюции событий: версионирование, апкастинг и совместимость без поломок у потребителей.

• Генеративный ИИ и .NET Aspire в действии


Интеграция LLM, управление контекстом и масштабируемая оркестрация с помощью Semantic Kernel и Aspire.

• Функциональное программирование в F#: мода или польза


Реальные преимущества функционального подхода — лаконичные пайплайны, паттерн-матчинг и безопасные абстракции.

• Как нашли утечку 2 ГБ в день за 5 минут


История о том, как обнаружить гигабайтные утечки памяти с помощью dotMemory в продакшене за считанные минуты.

Два дня — и десятки инсайтов о будущем .NET: от облаков и AI до функционального подхода и устойчивой архитектуры. Записи доступны на YouTube.

➡️ Первый день
➡️ Второй день

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
7🔥4👍3
🔍 Как работать с массивами в C# без тормозов и без unsafe-кода

В C# каждое обращение к элементу массива проверяется на выход за границы. Это безопасно, но медленно. Разработчики часто решают проблему через unsafe-код с указателями — быстро, но опасно: один неверный индекс, и приложение крашится или получает дыру в безопасности.

Есть третий путь — Span<T>

Span — это структура, которая хранит указатель на данные и их длину. Фишка в том, что это ref struct — она живёт только в стеке и не может попасть в кучу. Благодаря этому компилятор гарантирует: данные переживут span, а значит проверки границ можно убрать.

Например, в быстрой сортровке вместо передачи массива с индексами low/high передаёте span. Код короче, переполнение невозможно, а рекурсивные вызовы работают через срезы:
// Было: опасно, (low + high) может переполниться
void Quicksort(int[] array, int low, int high) {
int mid = (low + high) / 2; // 💥 overflow!
// ...
Quicksort(array, low, pivot - 1);
Quicksort(array, pivot + 1, high);
}

// Стало: безопасно и выразительно
void Quicksort(Span<int> span) {
if (span.Length <= 1) return;

int pivot = Partition(span);
Quicksort(span[..pivot]); // левая часть
Quicksort(span[(pivot + 1)..]); // правая часть
}


Раньше для передачи части массива в функцию приходилось либо копировать данные, либо таскать массив + offset + length. Span решает это элегантно: создаёте срез array[10..20], передаёте дальше — никакого копирования, полная безопасность типов.

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1711😁1