C # 7: Підкреслення (_) та зірочка (*) у змінній Out


79

Я читав про нові функції змінних в C # 7 тут . У мене є два запитання:

  1. Він говорить

    Ми також дозволяємо "відкидати" як вихідні параметри у формі a _, щоб дозволити вам ігнорувати параметри, які вас не хвилюють:

    p.GetCoordinates(out var x, out _); // I only care about x
    

    З: Я припускаю, що це лише інформація, а не нова функція C # 7, тому що ми можемо зробити це і в попередній C # 7.0:

    var _;
    if (Int.TryParse(str, out _))
    ...
    

    чи мені чогось тут не вистачає?

  2. Мій код видає помилку, коли я роблю, як згадується в тому ж блозі:

    ~Person() => names.TryRemove(id, out *);
    

    *не є дійсним ідентифікатором. Нагляд Мадса Торгерсена, мабуть?


15
in out _ _не є змінною, ви не заявляєте її і не можете використовувати її по імені. У int _цьому є змінна.
Evk,

9
Здається, підстановка зірочки не потрапила до остаточного випуску C # 7, здається.
Майкл Штум

3
@NikhilAgrawal, але це інший синтаксис. У своєму питанні ви використовуєте out _, без var. З varцим справді так само, як і раніше.
Evk,

2
@NikhilAgrawal, який справді компілюється з якоїсь дивної причини. Однак, якщо поглянути на декомпільований код - цього призначення просто немає, повністю видалено. І якщо ви зробите щось подібне Console.WriteLine(_)- це не буде компілювати, стверджуючи, що такої змінної немає. Досить дивно. Навіть більше: якщо ви зробите щось подібне _ = SomeMethodCall()- це буде замінено на просто SomeMethodCall()зібраний код. Тож врешті-решт ви все ще не можете використовувати цю змінну в будь-якому значущому сенсі.
Evk,

Відповіді:


111

Викиди в C # 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

Виняток з цього становить, коли _змінна використовується як вихідна змінна. У цьому випадку компілятор ігнорує тип або varрозглядає його як відкидання:

if (p.GetCoordinates(out double x, out double _))
{
    _ = "hello"; // works fine.
    Console.WriteLine(_); // error: _ doesn't exist in this context.
}

Зауважте, що це відбувається лише в тому випадку, якщо в цьому випадку використовується out var _або out double _використовується. Просто використовуйте, out _і тоді це трактується як посилання на існуючу змінну _, якщо вона знаходиться в області дії, наприклад:

string _;
int.TryParse("1", out _); // complains _ is of the wrong type

Нарешті, *позначення було запропоновано на початку дискусій навколо викидів, але було відмовлено на користь, _оскільки остання є більш вживаною нотацією іншими мовами .


Я думаю, ви маєте на увазі "... з- _за того, що останній був ..."
martijnn2008

@ martijnn2008, добре помічений. Дякую.
Девід Арно,

1
Я припускаю, що це мається на увазі, але сенс відкидання полягає в тому, що його потенційна вартість насправді ніколи не зберігається?
Сінджай

Заявлення про те, що _ = 42"відкинути [результат] виразу" є оманливим, оскільки _ = 42саме по собі є виразом зі значенням 42, тому фактичного відкидання не відбувається. Все ще є різниця, оскільки _ = 42;це також твердження, тоді 42;як ні, що має значення в деяких контекстах.
Jeroen Mostert

1
Фраза чудова, просто вона _ = 42не може показати, в чому сенс цього відкидання - тобто коли вам потрібно буде "не зберігати" вираз, а все одно його оцінити, бачачи, як ви зазвичай можете оцінити (нетривіальний , корисний) вираз просто чудовий, не зберігаючи його. Я не можу відразу придумати корисний приклад (і я не знаю, чи є такий, чи це лише результат послідовності в граматиці).
Jeroen Mostert

30

Іншим прикладом оператора Discard _у C # 7 є узгодження шаблону змінної типу objectв switchоператорі, який нещодавно був доданий у C # 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 ... _.


14

Для більш цікавих

Розглянемо наступний фрагмент

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, класна річ полягає в тому, що вам не потрібно попередньо оголошувати змінні, що зручно, якщо вас не цікавлять деякі значення.


3
IL має такий же , тому що функція ви дзвоните по раніше вимагає слота для змінних з. Просто використання нового синтаксису відкидання дозволяє компілятору робити подальші припущення щодо локальної змінної (вірніше, про відсутність), дозволяючи їй ефективніше використовувати її (принаймні теоретично; я не знаю, чи вже є якісь оптимізації в компіляторі на даний момент).
тикати

9

Щодо першого питання

Я припускаю, що це лише інформація, а не нова функція C # 7, тому що ми можемо зробити це в попередній C # 7.0 теж.

var _;
if (Int.TryParse(str, out _))
    // ...

Новизна полягає в тому, що вам не потрібно _більше заявляти всередині або поза виразом, а ви можете просто ввести

int.TryParse(s, out _);

Спробуйте зробити цей один лайнер до C # 7:

private void btnDialogOk_Click_1(object sender, RoutedEventArgs e)
{
     DialogResult = int.TryParse(Answer, out _);
}

7
Додамо: Підкреслення дуже добре працює для методів із кількома параметрами out, наприклад, SomeMethod(out _, out _, out three)має 3 параметри out, але я викидаю перші два без необхідності створювати такі змінні, як unused1, unused2і т. Д.
Michael Stum

@MichaelStum: Що тут відбувається? if (SomeMethod(out _, out _, out _)) _ = 5; До чого _це стосується?
Nikhil Agrawal

4
@NikhilAgrawal Не було б _змінної взагалі, навіть якби ти використовував out var _. Здається, підкреслення особливе, щоб відкинути результат.
Michael Stum

0

У C # 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;

}


0

З: ... ми можемо зробити це і в попередній C # 7.0:

var _;
if (Int.TryParse(str, out _))

чи мені чогось тут не вистачає?

Це не одне й те саме.
Ваш код робить призначення.

У C # 7.0 _ не є змінною, він повідомляє компілятору відкинути значення
( якщо ви не оголосили _ як змінну ... якщо ви зробите так, що змінна використовується замість символу відкинути)

Приклад: ви можете використовувати _ як жало та int в одному рядку коду :

string a; 
int b;
Test(out a, out b);
Test(out _, out _);

//...

void Test(out string a, out int b)
{
   //...
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.