TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #3065 2.27K
День 2549. #ЗаметкиНаПолях
Передавайте Состояние. Окончание

Начало
Продолжение

CancellationToken.Register
Вы можете зарегистрировать функцию обратного вызова, которая будет вызвана при отмене токена:
CancellationToken ct;
TaskCompletionSource tcs = new ();
ct.Register(() => tcs.TrySetCanceled());

Та же проблема. Попытка добавить модификатор static приведёт к ошибке:
ct.Register(static () => tcs.TrySetCanceled());

Однако, если создатель функции подумал о производительности, он предоставит перегрузку, принимающую состояние.
…
ct.Register(
static (state) =>
((TaskCompletionSource)state).TrySetCanceled(),
tcs); // Состояние передаётся в последнем параметре

Да, здесь требуется привести состояние к нужному типу и правильно его передать. В обычном бизнес-приложении это может быть необязательно. Но, например, в RavenDB, распределённой БД, созданной на .NET и использующей CancellationToken из BCL, PR #21205 предлагает такие улучшения, используя перегрузку с передачей состояния.

Принцип улучшений прост:
- Вы замечаете использование Register с нестатическии методом.
- Делаете лямбду статической и ждёте ошибок компилятора.
- При возможности используйте перегрузку с передачей состояния.

Task.ContinueWith
Аналогичный подход, как и с CancellationToken, можно использовать с продолжениями, которые можно прикреплять к задачам вручную. Иногда необходимо прикрепить продолжение к задаче без использования сложного механизма async-await. В таком случае можно использовать метод ContinueWith:
Task connection;
TaskCompletionSource tcs;

connection.ContinueWith ( (t) => {
if (t.IsFaulted)
tcs.TrySetException(t.Exception);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(null);
});

Мы можем следовать тому же подходу, сначала сделав метод статическим. Затем, обратив внимание на ошибку компиляции, исправим её с помощью перегрузки метода, передающей состояние, и приведения типов:
Task connection;
TaskCompletionSource tcs;

// лямбда теперь статическая
connection.ContinueWith ( static (t, state) => {
// приводим к типу продолжения
var c = (TaskCompletionSource) state;
if (t.IsFaulted)
c.TrySetException(t.Exception);
else if (t.IsCanceled)
c.TrySetCanceled();
else
c.TrySetResult(null);
}, tcs); // передаём состояние


Итого
Захват переменных в лямбдах может стать серьёзной проблемой на горячих путях приложений .NET. К счастью, существуют контрмеры в виде статических лямбда-выражений и перегрузок, передающих состояние, которые позволяют устранить эти проблемы.

Источник: https://blog.scooletz.com/2025/10/08/pass-the-state
  • 👍 5
More from @netdeveloperdiary
  1. Sep 28, 2026День 2798. #Оффтоп Утиная Типизация в C# с Помощью Перехватчиков. Часть 2 Некоторое время…
  2. Sep 27, 2026День 2797. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Окончание Начало Продол…
  3. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  4. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
  5. Sep 24, 2026День 2794. #Оффтоп #Здоровье Сегодня будет необычный пост. Завтра в Москве стартует конфер…
  6. Sep 23, 2026День 2793. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →