Яка різниця між перемиканням та відмовою в процесі виконання?


15

C # надає ключове слово refта outключове слово, щоб зробити аргументи для передачі посилань. Смисловість двох дуже схожа. Єдина відмінність полягає в ініціалізації флагізованої змінної:

  • refвимагає, щоб змінна була ініціалізована перед передачею її у функцію, outне робить.
  • outвимагає, щоб змінна була ініціалізована всередині функції, refне робить.

Випадки використання цих двох ключових слів також майже однакові, і їх занадто часте використання, я вважаю, вважається запахом коду (хоча є дійсні випадки використання, такі як TryParseі TryGetValueшаблони).

Через це міг би хтось пояснити, чому в C # є два дуже схожі інструменти для таких вузьких випадків використання?

Також в MSDN зазначається, що вони мають різну поведінку під час виконання:

Хоча ключові слова ref і out викликають різну поведінку під час виконання, вони не вважаються частиною підпису методу під час компіляції.

Чим відрізняється їх поведінка під час виконання?

Висновок

Обидві відповіді виглядають правильно, дякую обом. Я прийняв jmoreno 's, тому що це більш явно.


Я здогадуюсь, що вони обидва реалізовані так само, як і refв C ++; на вказівники на предметний вказівник (або примітивний), про який йде мова. тобто Int32.TryParse(myStr, out myInt)(C #) "виконується" так само, як і int32_tryParse(myStr, &myInt)(C); Єдина відмінність полягає в деяких обмеженнях, що застосовуються компілятором для запобігання помилок. (Я не збираюся розміщувати це як відповідь, тому що я можу помилятися з приводу того, як це працює за лаштунками, але я вважаю, що це працює [бо це має сенс])
Коул Джонсон

Відповіді:


10

На той момент, коли це питання було задано, стаття MSDN була неправдивою (вона з тих пір була виправлена ). Замість того, щоб "викликати іншу поведінку", вона повинна бути "вимагає різної поведінки".

Зокрема, компілятор виконує різні вимоги до двох ключових слів, хоча той самий механізм (IL) використовується для активізації поведінки.


Тож я розумію, що компілятор перевіряє такі речі, як перенаправлення outпараметра або не присвоєння refпараметра, але IL, який він випускає, є лише посиланням у будь-якому випадку. Це правильно?
Андрій

До речі, це можна сказати з того факту, що out Tі ref Tнадаються однаково в інших мовах CLI, таких як VB.Net або C ++ / CLI.
ACH

@AndrewPiliser: правильно. Якщо припустити, що код складається в будь-якому випадку (ви не намагаєтесь знешкодити, коли не слід), IL буде ідентичним.
jmoreno

4

ось цікавий опис теми, який може відповісти на ваше запитання:

http://www.dotnetperls.com/ref

цікава точка:

"Різниця між відмовою і відсутньою полягає не в загальній мові виконання, а в самій мові C #."

оновлення:

старший розробник моєї роботи просто підтвердив відповідь @ jmoreno, тому переконайтеся, що ви її прочитали !.


Отже, якщо я правильно розумію, різниці на рівні ІЛ немає, що означає, що вони не можуть мати різну поведінку часу виконання. Це суперечить тому, що говорить MSDN. Цікава стаття все-таки!
Gábor Angyal

@ GáborAngyal ви випадково не маєте посилання на статтю MSDN? Може бути приємно, що тут, якщо думки будуть різними
Dan Beaulieu

Ось ви йдете: msdn.microsoft.com/en-gb/library/t3c3bfhx.aspx , але все це було в моєму питанні :)
Gábor Angyal

1
Що стосується stringвсього цього?
Роберт Харві

Це просто парафраза зі статті. Оновлено, щоб видалити другий рядок ..
Дан Болье

2

Виконання ? Абсолютно ніхто. Ви не можете перевантажувати метод з однаковими параметрами, що відрізняється лише ключовим словом ref або out.

Спробуйте скласти це, і ви отримаєте помилку компіляції "Метод з такою ж підписом вже оголошено":

    private class MyClass
    {
        private void DoSomething(out int param)
        {
        }
        private void DoSomething(ref int param)
        {
        }
    }

Щоб відповісти на це запитання: "... чому в C # є два дуже схожі інструменти для таких вузьких випадків використання?"

Від читабельності коду та точки зору API існує величезна різниця. Як споживач API, я знаю, що при використанні "out" API не залежить від параметра out. Як розробник API, я віддаю перевагу "вийти" і використовую "ref" лише тоді, коли абсолютно необхідно (Рідко!). Дивіться цю посилання для чудового обговорення:

/programming/1516876/when-to-use-ref-vs-out

Інформація про підтримку: я склав наступний метод і розібрав його. Я використовував ключові слова ref та out (у цьому прикладі), але код складання не змінився, за винятком посилання на адресу, як я очікував:

    private class MyClass
    {
        internal void DoSomething(out int param)
        {
            param = 0;
        }
    }

00000000 push ebp
00000001 mov ebp, esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp, 34h
00000009 xor eax, eax
0000000b mov dword ptr [ebp-10h], eax
0000000e mov dword ptr [
ebp mov dword ptr [ebp-3Ch], ecx
00000014 mov dword ptr [ebp-40h], edx
00000017 cmp dword ptr ds: [008B1710h], 0
0000001e je 00000025
00000020 call 6E6B601E
00000025 nop
param = 0;
00000026 mov eax, dword ptr [ebp-40h]
00000029 xor edx, edx
0000002b mov dword ptr [eax], edx
}
0000002d nop
0000002e lea esp, [ebp-0Ch]
00000031 pop
ebx 00000032 pop esi
00000033 pop edi
00000034 pop
ebp 00000035 ret

Чи правильно я читаю збірку?


Те, що ви пишете, є правильним, але зауважте, що це не очевидно, що немає різниці у виконанні, оскільки ця перевантаження не дозволена.
Gábor Angyal

А-а - хороший момент! Чи може хтось розібрати код вище та довести мене правильно чи неправильно? Я страшенно читаю збори, але цікаво знати.
Шмокен

Гаразд - я розібрав код за допомогою "out" та "ref", і я не бачу різниці для свого методу DoSomething:
Shmoken
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.