С# 7: Подчеркивание (_) & Star (*) в переменной Out
Я читал о новых возможностях переменных в С# 7 здесь. У меня есть два вопроса:
-
Это говорит
Мы также разрешаем "отбрасывать" как параметры out, в форме _
, чтобы позволить вам игнорировать параметры, которые вас не интересуют:
p.GetCoordinates(out var x, out _); // I only care about x
Q: Я думаю, это просто информация, а не новая функция С# 7, потому что мы можем сделать это и в С# 7.0:
var _;
if (Int.TryParse(str, out _))
...
или я что-то здесь упускаю?
-
Мой код выдает ошибку, когда я делаю, как указано в том же блоге:
~Person() => names.TryRemove(id, out *);
*
не является допустимым идентификатором. Полагаю, Мэдс Торгерсен, наверное, недосмотр?
Ответы
Ответ 1
Отбрасывает, в С# 7 может использоваться везде, где объявлена переменная, чтобы - как следует из названия - отбросить результат. Таким образом, сброс можно использовать без переменных:
p.GetCoordinates(out var x, out _);
и его можно использовать для отбрасывания результата выражения:
_ = 42;
В этом примере
p.GetCoordinates(out var x, out _);
_ = 42;
Переменная _
не вводится. Есть только два случая использования сброса.
Однако, если в области существует идентификатор _
, сбросы использовать нельзя:
var _ = 42;
_ = "hello"; // error - a string cannot explicitly convert from string to int
Исключением является случай, когда переменная _
используется как переменная out. В этом случае компилятор игнорирует тип или var
и рассматривает его как сброс:
if (p.GetCoordinates(out double x, out double _))
{
_ = "hello"; // works fine.
Console.WriteLine(_); // error: _ does not exist in this context.
}
Обратите внимание, что это происходит только в том случае, если в этом случае используется out var _
или out double _
. Просто используйте out _
, а затем он будет рассматриваться как ссылка на существующую переменную, _
, если она находится в области видимости, например:
string _;
int.TryParse("1", out _); // complains _ is of the wrong type
Наконец, нотация *
была предложена в начале обсуждения сбрасываний, , но была отклонена в пользу _
из-за того, что последняя является более широко используемой нотацией на других языках.
Ответ 2
Другой пример оператора сброса _
в С# 7 - это сопоставление с образцом переменной типа object
в операторе switch
, который недавно был добавлен в С# 7:
Код:
static void Main(string[] args)
{
object x = 6.4;
switch (x)
{
case string _:
Console.WriteLine("it is string");
break;
case double _:
Console.WriteLine("it is double");
break;
case int _:
Console.WriteLine("it is int");
break;
default:
Console.WriteLine("it is Unknown type");
break;
}
// end of main method
}
Этот код будет соответствовать типу и отбрасывать переменную, переданную в case ... _
.
Ответ 3
Для более любопытных
Рассмотрим следующий фрагмент
static void Main(string[] args)
{
//....
int a;
int b;
Test(out a, out b);
Test(out _, out _);
//....
}
private static void Test(out int a, out int b)
{
//...
}
Это то, что происходит:
...
13: int a;
14: int b;
15:
16: Test(out a, out b);
02340473 lea ecx,[ebp-40h]
02340476 lea edx,[ebp-44h]
02340479 call 02340040
0234047E nop
17: Test(out _, out _);
0234047F lea ecx,[ebp-48h]
02340482 lea edx,[ebp-4Ch]
02340485 call 02340040
0234048A nop
...
Как вы можете видеть позади сцены, два вызова делают одно и то же.
Как сказал @Servé Laurijssen, дело в том, что вам не нужны переменные pre-declare, которые удобны, если вас не интересуют некоторые значения.
Ответ 4
Относительно первого вопроса
Я думаю, это просто информация, а не новая функция С# 7, потому что мы можем сделайте это и в pre С# 7.0.
var _;
if (Int.TryParse(str, out _))
// ...
Новизна в том, что вам больше не нужно объявлять _
внутри или вне выражения, и вы можете просто ввести
int.TryParse(s, out _);
Попробуйте сделать это один лайнер pre С# 7:
private void btnDialogOk_Click_1(object sender, RoutedEventArgs e)
{
DialogResult = int.TryParse(Answer, out _);
}
Ответ 5
В С# 7.0 (Visual Studio 2017 вокруг марта 2017 г.) отбрасывания поддерживаются в назначениях в следующих контекстах:
Другие полезные заметки
- сбросы могут уменьшить выделение памяти. Потому что они делают намерение
из вашего кода ясно, они улучшают его читаемость и удобство обслуживания
- Обратите внимание, что _ также является действительным идентификатором. При использовании вне
поддерживаемый контекст
Простой пример: здесь мы не хотим использовать 1-й и 2-й параметры, нам нужен только 3-й параметр
(_, _, area) = city.GetCityInformation(cityName);
Продвинутый пример в случае коммутатора, в котором также используется современное сопоставление с шаблоном коммутатора (источник)
switch (exception) {
case ExceptionCustom exceptionCustom:
//do something unique
//...
break;
case OperationCanceledException _:
//do something else here and we can also cast it
//...
break;
default:
logger?.Error(exception.Message, exception);
//..
break;
}