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

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

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

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

РКН: https://gosuslugi.ru/snet/67a5c81cdc130259d5b7fead
Download Telegram
🤨 Когда помощник становится врагом

Представьте, что вы отправили друга в магазин с деньгами на общий счёт. Он может потратить их на что угодно, при этом никому не отчитываясь. Вот примерно так же работает static с изменяемым состоянием. Удобно? Да. Безопасно? Совсем нет.

Почему static плохо сочетается с данными

Static — это ключевое слово, которое любят новички и используют для удобства. А потом из этого вырастают проблемы, которые отъедают часы отладки. Главная беда в том, что статические поля существуют всё время работы программы и доступны отовсюду.

Четыре золотых правила

1. Статические поля могут быть только для чтения. Лучше — константы.

2. Статические методы должны работать как математические функции: дали параметры — получили результат. Никаких побочных эффектов, никаких изменений состояния. Например: Select, Where, First из LINQ — это всё статические методы расширения, которые не трогают исходную последовательность.

3. Если в классе есть методы, которые не используют поля экземпляра, эту логику лучше вынести в отдельный статический класс-помощник. Это делает код чище, тесты проще.

4. Если вы создаёте статическое поле, пусть оно указывает на что-то, что не меняется. На объект конфигурации? Хорошо. На список данных, который вы потом модифицируете? Катастрофа.

Когда static — ваш друг

• Вспомогательные классы с чистыми функциями

Создайте статический класс StringUtils с методом static string ToPascalCase(string input). Нет состояния, нет побочных эффектов, нет проблем. Это хорошее использование.

• Методы расширения для преобразований

Select, Where и компания — статические методы расширения, которые берут данные, преобразуют их и возвращают новые. Функциональный подход, никакого волшебства.

• ThreadStatic для действительно отдельного состояния

Если каждому потоку нужно своё состояние, можно использовать ThreadStatic. Но даже здесь нужна осторожность — каждый поток всё равно должен управлять своим состоянием.

Передайте зависимость через конструктор, используйте DI-контейнер, создайте объект на сессию. Ваш будущий я, сидящий в отладчике в три утра, скажет вам спасибо.

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
Расширяем типы по-новому

В C# 14 расширяется концепция расширений: можно создавать не только расширенные методы для экземпляров типов, но и свойства расширения, а также расширения, которые относятся к самому типу как к статическому элементу.

Пример:
public static class EnumerableExtensions
{
extension<TSource>(IEnumerable<TSource> source)
{
public bool IsEmpty => !source.Any(); // Расширенное свойство
}

extension<TSource>(IEnumerable<TSource>)
{
public static IEnumerable<TSource> Combine(IEnumerable<TSource> first, IEnumerable<TSource> second)
=> first.Concat(second); // Статический метод расширения
}
}


Конструкция extension<TSource>(IEnumerable<TSource> source) — это новый синтаксис блока расширения для экземпляра типа: здесь определяются расширяющие члены, которые «прикрепляются» к объекту IEnumerable<TSource>.
Внутри блока объявлено свойство IsEmpty, которое возвращает true, если последовательность пуста (!source.Any()).

Второй блок extension<TSource>(IEnumerable<TSource>) — расширение для самого типа IEnumerable<TSource> как статического члена.
Внутри него определён статический метод Combine, который принимает две последовательности и объединяет их с помощью Concat.

Как это использовать:
var list = new List<int> { 1, 2, 3 };

bool empty = list.IsEmpty; // false — вызов расширенного свойства как у экземпляра

var combined = Enumerable<int>.Combine(new[] { 1 }, new[] { 2, 3 }); // {1, 2, 3}


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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥53😁3
⚡️ Ключевое слово field упрощает свойства

Управление данными в свойствах теперь проще с новым ключевым словом для доступа к автоматически созданному полю.

Раньше для добавления логики установки значения использовалось отдельное поле и аксессоры:
private string _msg;
public string Message
{
get => _msg;
set => _msg = value ?? throw new ArgumentNullException(nameof(value));
}


Сейчас field ссылается на поле, которое создает компилятор, сокращая код:
public string Message
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(value));
}


Минимум кода — максимум контроля: write-only свойства снаружи и строгая логика внутри.

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍212😁2
🛠 Натуральные преобразования для Span и ReadOnlySpan

C# 14 позволяет неявно преобразовывать между Span<T>, ReadOnlySpan<T> и массивами, упрощая манипуляции с данными и повышая скорость:
ReadOnlySpan<char> roSpan = "hello";
Span<char> span = roSpan;
char[] array = roSpan.ToArray();


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

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥821
⚡️ ConfigureAwait(false) — зачем это нужно

Вы пишете библиотеку на C#, используете async/await, и всё работает. Но потом ваша библиотека попадает в веб-приложение, мобильное приложение, или какой-то другой контекст — и начинаются странные deadlock'и или даже зависания.

Проблема вот в чём: когда код выходит из await, он хочет продолжить выполнение в том же контексте, в котором был запущен. Если это UI-приложение, контекст одноточечный — только главный поток может работать с UI.

Если ваша библиотека захватит этот контекст и не отпустит — приложение зависнет. А если написали синхронный код, который вызывает async библиотеку, и она захватит контекст — будет deadlock.

ConfigureAwait(false) как раз говорит: «Мне вообще всё равно, в каком контексте продолжать. Возьми любой поток из thread pool'а«. И вот это спасает приложения от проблем:
await stream.WriteAsync(buffer, ct).ConfigureAwait(false);


Результат: библиотека не блокирует контекст, не создаёт deadlock'и, работает везде одинаково.

Когда НЕ нужен ConfigureAwait(false):

В приложениях можно писать без него, если вам нужен контекст:
// В ASP.NET контроллере — можно без ConfigureAwait(false)
[HttpGet]
public async Task<IActionResult> GetData()
{
var data = await service.GetDataAsync(); // Контекст нужен для логирования, etc
return Ok(data);
}

// В UI приложении — нужен контекст
private async void Button_Click(object sender, EventArgs e)
{
var data = await LoadDataAsync(); // Нужно вернуться в UI поток
UpdateUI(data);
}


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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
12👍10🥱1
🧑‍💻 Контролируем параллельную работу

Представьте ситуацию: у вас есть 1000 элементов для обработки, и вы хотите запустить это параллельно. Но если просто запустить все одновременно, приложение упадёт — будет слишком много одновременных подключений к БД, API или файловой системе.

Здесь помогает SemaphoreSlim.

Как это работает

Создаём семафор с лимитом 4. Это значит, что максимум 4 потока могут находиться внутри защищённого блока одновременно.
await gate.WaitAsync(ct);


Поток ждёт в очереди, пока не появится "место". Если уже 4 потока внутри — новичок подождёт:
await gate.WaitAsync(ct);


После обработки обязательно вызываем Release() — это освобождает место для следующего потока. finally гарантирует, что это произойдёт даже если операция упадёт:
try { await Process(x, ct); }
finally { gate.Release(); }


Вместо 100 одновременных загрузок будет ровно столько, сколько вы укажете.

🔹 Алгоритмы и структуры данных
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
📎 Частичные события и конструкторы

Новый уровень модульности в больших проектах: делим объявление и реализацию. Большие проекты требуют таких гибких способов организации кода.

В C# теперь можно объявлять события и конструкторы с модификатором partial. Это значит, что объявление может находиться в одном месте, а его реализация — в другом, обычно в другом файле:
public partial class MyClass
{
partial void OnInitialized();

public MyClass()
{
OnInitialized();
}
}


Здесь объявлена частичная функция OnInitialized() без тела — её реализация может появиться отдельно. Также конструктор вызывает эту функцию, если она реализована.

Частичные конструкторы поддерживают совместимость с платформами, использующими генерацию кода.

🔹 Практический интенсив «Архитектуры и шаблоны проектирования»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔42🥱1
👨‍💻 Когда нужна стабильность, а не спешка

Представьте фоновую задачу, которая должна срабатывать каждые 5 секунд: проверка очереди, синхронизация данных, отправка метрик. Можно, конечно, использовать Task.Delay, но это хрупко — если одна итерация длится дольше, чем ожидалось, ритм собьётся.

PeriodicTimer решает эту проблему иначе:
var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));


Создаём таймер, который будет срабатывать каждые 5 секунд. Точка отсчёта — момент создания.

while (await timer.WaitForNextTickAsync(ct))
{
await DoWork(ct);
}


WaitForNextTickAsync ждёт следующего тика таймера. Когда он приходит — выполняем работу. Если работа заняла 1 секунду, следующий тик всё равно придёт через 5 секунд от начала цикла, а не через 6.

Это стабильный интервал, а не интервал между завершением одной работы и началом другой.

🔹 Практический интенсив «Архитектуры и шаблоны проектирования»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥19👍7
🛠 Как создать путь к временному файлу в C#

В C# для работы с путями к файлам существует класс Path, который предлагает методы для объединения частей путей и получения системных директорий. Один из таких методов — GetTempPath(). Он возвращает путь к папке, предназначенной для временных файлов на текущей системе.

Другой полезный метод — Combine(). Он соединяет несколько частей пути, учитывая особенности слэшей в Windows, Linux и macOS. Вместо ручного склеивания строк и риска ошибок, Combine() гарантирует правильный формат.

Пример:
using System;
using System.IO;

class Program
{
static void Main()
{
// Получаем путь к системной временной папке и добавляем имя файла
var path = Path.Combine(Path.GetTempPath(), "report.csv");
Console.WriteLine("Путь к временному файлу: " + path);

// Здесь можно сохранить файл, открыть или выполнить другие операции
}
}


Использование Path.GetTempPath() и Path.Combine() — простой способ получить корректный путь к временному файлу без ошибок и с учётом особенностей операционных систем.

🔸 Основы IT для непрограммистов
🔸 Получить консультацию менеджера
🔸 Сайт Академии 🔸 Сайт Proglib

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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🥱7
✏️ Null-условное присваивание

В обыденной работе с объектами часто приходится проверять, не равен ли объект null, прежде чем присваивать значение его свойствам. Это приводит к громоздким конструкциям с множеством if. Теперь с null-условным присваиванием можно написать проще и понятнее.

Операторы условного доступа ?. и ?[] теперь поддерживают использование слева от оператора присваивания. Раньше для безопасного присваивания требовалось писать так:
if (customer is not null)
{
customer.Order = GetCurrentOrder();
}


С null-условным присваиванием можно упростить до одной строки:
customer?.Order = GetCurrentOrder();


Особенность в том, что правая часть вычисляется только если левая часть не равна null. Если customer равен null, метод GetCurrentOrder() вызван не будет. Это оптимизирует производительность и упрощает логику.

Также это работает с составными операторами присваивания: +=, -=, *=, но не поддерживается для инкремента и декремента.
customer?.Total += 100;
// Но нельзя так:
// customer?.Total++;


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

#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🥱5