TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #3063 2.17K
День 2547. #ЗаметкиНаПолях
Передавайте Состояние. Начало
Представьте, что вы создали простую функцию фильтрации:
public IEnumerable<Item> 
GetOversized(IEnumerable<Item> items)
=> items.Where(item => item.Size > 45);

А затем решили, что нужно добавить параметр. Интуиция предлагает простое решение:
public IEnumerable<Item> 
GetOversized(IEnumerable<Item> items, int minSize)
=> items.Where(item => item.Size > minSize);


Для фрагментов кода, которые выполняются редко, это будет работать. А на горячем пути? Или в параллельном коде с отменой или продолжением? Давайте разберёмся!

Лямбда-функции
C# позволяет нам определить анонимную функцию, которую можно передать как Func или Action. Сначала определяем параметры, а потом тело функции после стрелки:
item => item.Size > minSize;

Есть одна проблема: эти функции захватывают контекст, т.е. переменные или параметры из окружения. В реальности компилятор создаёт класс, поля которого содержат необходимые данные (в примере выше – переменную minSize).

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

В C# 9.0 добавлены статические анонимные функции, которые запрещают захват контекста. Однако, в примере выше это приведёт к ошибке компиляции:
// Нельзя использовать static и minSize вместе
items.Where(static (item) => item.Size > minSize);

Не всегда возможно избежать захвата контекста. Для использования статических анонимных функций необходимо выполнение одного из двух условий:
1. Функция требует только предоставленных параметров.
2. Существует перегрузка, которая принимает состояние, передаваемое в качестве дополнительного параметра.

Рассмотрим второе условие подробнее:
public IEnumerable<T> Query(Func<T, bool> predicate);

Очевидно, что, если для предиката требуется какое-либо внешнее состояние, потребуется захватить его. А в следующей перегрузке:
public IEnumerable<T> Query<TContext>(
Func<T, TContext, bool> predicate,
TContext ctx);

…добавлен дополнительный параметр, называемый контекстом, который можно передать функции. Суть в том, что он также передается предикату. Если у вас есть чётко определённый контекст, вы можете просто передать его в качестве дополнительного параметра:
repo.Query(
// ctxMinSize передаст параметр minSize в лямбду
static (item, ctxMinSize) =>
(item) => item.Size > ctxMinSize,
minSize);

Теперь мы знаем, что, объединив статические лямбда-функции и параметр контекста, мы можем многого добиться, не захватывая состояние (не выделяя память). Далее рассмотрим примеры.

Продолжение следует…

Источник:
https://blog.scooletz.com/2025/10/08/pass-the-state
  • 👍 20
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 →