День 1339. #ЗаметкиНаПолях
Пустые Переменные в Лямбдах
В твиттере задали интересный вопрос:
Имеет ли смысл использование пустой переменной (дискарда) `_` в выражении типа?
( _ , args) =>{…}
Я ожидаю параметр, но не буду с ним ничего делать.
— Layla #WomenOfDotNet (@LaylaCodesIt)
Короткий ответ: да. Но кому нужны простые ответы? Пустые переменные появились в C# недавно. А это значит, что не всё так просто, как кажется.
В C# есть несколько мест, где мы используем _ как пустую переменную. В примере ниже _ технически не является ей:
Func<int, int, int> f = (_, x) => 2 * x;
Лямбда здесь имеет два параметра, первый называется _, а второй называется x. C# принимает _ в качестве идентификатора в соответствии с правилами языка. Даже Visual Studio при наведении курсора на _ показывает подсказку «(parameter) int _». Мы можем продемонстрировать это, используя оба параметра:
Func<int, int, int> f2 = (_, x) => _ * x;
Следующий код не работал в C#8, но C#9 позволяет так писать:
Func<int, int, int> f3 = (_, _) => 42;
Здесь символы подчеркивания действительно являются пустыми переменными. (Правило состоит в том, что если лямбда имеет несколько параметров с именем _, то все они отбрасываются). A Visual Studio при наведении курсора на _ показывает подсказку «(discard) int _». Однако это работает только для лямбда-выражений.
Зачем это нужно?
Иногда мы вынуждены принимать аргументы, которые не будем использовать. Неиспользуемые параметры выглядят так, как будто это может быть ошибкой:
void ShowNumber(int i)
{
Console.WriteLine(42);
}
Анализатор кода не выдаёт даже предупреждения, а выдаёт только сообщение IDE0060 уровня Message о неиспользуемой переменной. Однако читателю этого кода неочевидно, есть ли ошибка в коде метода или так и задумано. При этом, если вы фанат чистых сборок — без предупреждений и сообщений анализатора, — тогда изменения кода, приводящие к появлению новых сообщений, немедленно привлекут ваше внимание.
Поэтому принято соглашение об использовании _ в качестве имени параметра, который бы намеренно игнорировался. Если вы измените предыдущий пример, переименовав параметр i в _, вы увидите, что компилятор перестанет выдавать сообщение IDE0060, поскольку он знает об этом соглашении:
void ShowNumber(int _)
Но, если у вас есть два параметра, которые вы хотите игнорировать:
void ShowNumber(int _, int _)
это вызовет ошибку компиляции CS0100 из-за дублирующего имени параметра _, т.к. пустые переменные можно использовать только в лямбдах.
Использование _ - это просто соглашение. Это валидное имя для параметра, поэтому у нас не может быть двух параметров с именами _ по той же причине, по которой мы не можем иметь два параметра с именем i.
Всё становится ещё интереснее, когда _ используется в качестве имени локальной переменной. Например, что, по-вашему, выведет код с картинки ниже в C#9+?
Источник: https://endjin.com/blog/2022/09/csharp-lambda-discards