Full Stack Blog – C# vs Java. Discards

26 March 2023

C# vs Java. Discards

Заметка о discards в C# - это скорре краткий пересказ документации с небольшими добавлениями.

C#

Что это такое?

Discards или "отмена" - это механизм, позволяющий пометить некоторое значение в коде как не используемое (placeholder).

Discards - это как переменная которой не присвоено значение и при попытке использовать такую переменную мы получим CS0103, "The name '_' doesn't exist in the current context".

You may want to ignore the result of an expression, one or more members of a tuple expression, an out parameter to a method, or the target of a pattern matching expression.

То есть, предполагается что мы можем это использова для игнорирования

  • результата выражения
  • один ли больше членов кортежа
  • out параметр метода
  • or the target of a pattern matching expression - т.е. можем использовать как default ветку в switch expression

Discards и кортежи

static (double, double, double) GetXYZ() {
    return (1.0, 2.0, 3.0);
}
var (_, _, Z) = GetXYZ();

В этом примере мы пропускаем значения для X, Y и хотим дальше использовать только значение для Z и определяем переменную с именем "Z" для него.

Discards и switch выражения

int Value = 2;
string Message = Value switch
{
    1 => "Value == 1",
    2 => "Value == 2",
    _ => "Value >= 3"
};

это пример, показывающий, как можно использовать discards для определения "default" бранч в switch.

Также, мы можем использовать тип

string Message = Value switch
{
    AppEntity1 _ => "AppEntity1 type",
    AppEntity2 _ => "AppEntity2 type",
    _ => "other type"
};

Discards и "out" параметры метода

Можно "скипнуть" out параметры метода

Например

DateTime.TryParse("2022-05-01T14:57:32.8375298-04:00", out _)

Примеры использования

Проверка на null

Пример из документации. Очень лаконичный способ проверки переменной на null.

_ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");

здесь используется discard чтобы игнорировать результат выполнения выражения.

игнорировать объект Task

Пример из документации.

_ = Task.Run(() =>
   ...

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

Discards и параметры лямбд

Мы можем игнорировать пареметры лямбда выражений используя discard заполнители

(_, _) => { /* to do something*/ };

// or 
(int _, string _) => { /* to do something*/ };

Когда discard это не discard

Имя "_" - это валидное имя переменной, поэтому если оно использована не в контексте discard мы получим настоящую переменную с таким именем. Например

private static void ToDoSomething(string _)
{
    ...
}

мы сможем использовать такой вариант и работать с парметром "_" внутри метода ToDoSomething.

NOTE Не стоит так делать, так как это, с высокой вероятностью, приведет вас к ошибкам. Подробнее про ошибки - в документации.

Java

Нет. Не завезли.

Мы, конечно, можем написать так

var list = Stream.of(1, 2, 3)
                .filter(unusedParameter -> true)
                .collect(Collectors.toList());

Но такое решение - это не discard

Уже в пути

Возможно, эту фичу когда-нибудь имплементируют и в Java.

На момент openjdk-18.0.1.1, это выглядит так:

var list = Stream.of(1, 2, 3)
                .filter((Integer _) -> true)
                .collect(Collectors.toList());

// compile error: As of Java 9, '_' is a keyword, and may not be used as an identifier
var list = Stream.of(1, 2, 3)
                .filter(_ -> true)
                .collect(Collectors.toList());

// compile error: As of Java 9, '_' is a keyword, and may not be used as an identifier

В примере ниже - работает. Но это обычная переменная/параметр :)

var list3 = Stream.of(1, 2, 3)
                .filter(__ -> __ > 3)
                .collect(Collectors.toList());