Чи хороший підхід для виклику return усередині за допомогою оператора {}?


93

Я просто хочу знати, чи це безпечний / хороший підхід для дзвінка returnвсередині usingблоку.

Для екс.

using(var scope = new TransactionScope())
{
  // my core logic
  return true; // if condition met else
  return false;
  scope.Complete();
}

Ми знаємо, що в останню, найбільш кучеряву фігурну дужку dispose()будуть відкликані. Але що буде у наведеному вище випадку, оскільки returnконтроль виходить із заданого обсягу (AFAIK) ...

  1. Мені scope.Complete()дзвонять?
  2. І так для dispose()методу сфери дії .

1
Як тільки using{}область дії закінчиться, відповідні об'єкти будуть утилізовані, return"розіб'ють" область дії - отже об'єкти будуть віддалені, як очікувалося
Шай,

4
Майте на увазі, що ваш scope.Complete()дзвінок ніколи не потрапить за зразком, який ви надали, тому ваша транзакція завжди буде відмовлена.
Енді,

Незалежно від того, чи викликано using's dispose(), коли ви повернетесь, функція, що містить цей usingблок, повернеться, і все, що йому належить, залишиться сиротою. Отже, навіть якщо scopeвін не був утилізований "самим using" (він буде, як пояснили інші), він все одно буде утилізований, оскільки функція закінчилася. Якщо C # мав gotoзаяву - чи ти вже сміявся? добре - тоді замість повернення ви могли gotoб після закриття фігурної дужки, не повертаючись. Логічно, scopeщо все одно буде утилізовано, але ви щойно ввели gotoC #, тож хто дбає про логіку на цьому етапі.
Superbest


Відповіді:


145

Цілком безпечно дзвонити returnвсередину вашого usingблоку, оскільки використовуючий блок - це просто try/finallyблок.

У наведеному вище прикладі після повернення trueобласть дії буде утилізована, а значення повернене. return falseІ scope.Complete()буде НЕ додзвонилися. Disposeоднак буде викликаний незалежно від того, що він знаходиться всередині останнього блоку.

Ваш код по суті такий самий, як цей (якщо це полегшує розуміння):

var scope = new TransactionScope())
try
{
  // my core logic
  return true; // if condition met else
  return false;
  scope.Complete();
}
finally
{
  if( scope != null) 
    ((IDisposable)scope).Dispose();
}

Будь ласка, майте на увазі, що ваша транзакція ніколи не буде здійснена, оскільки немає можливості дійти до scope.Complete()її здійснення.


13
Ви повинні зробити це ясно , що Dispose буде викликатися. Якщо ОП не знає, що відбувається using, швидше за все, він не знає, що відбувається finally.
Конрад Рудольф

Нормально залишити використовуючи блок із поверненням, але у випадку TransactionScope у вас можуть виникнути проблеми з самим оператором using
thewhiteambit

На моєму досвіді, це не працює із збірками CLR SQL Server. Коли мені потрібно повернути результати для UDF, що містить поле SqlXml, яке посилається на об'єкт MemoryStream. Я отримую " Не вдається отримати доступ до утилізованого об'єкта " та " Неправильна спроба викликати Read, коли потік закритий. ", Тому я ЗМУШЕНИ писати негерметичний код і відмовитися від оператора using у цьому сценарії. :( Моя єдина надія - SQL CLR впорається з утилізацією цих об’єктів. Це унікальний сценарій, але я подумав би поділитися.
MikeTeeVee

1
@MikeTeeVee - Чистіші рішення: (a) абонент повинен зробити це using, наприклад using (var callersVar = MyFunc(..)) .., замість того, щоб використовувати всередині "MyFunc" - я маю на увазі, що абоненту надається потік і він відповідає за його закриття через usingабо явно, або (b) мати MyFunc витягувати будь-яку інформацію, необхідну для інших об’єктів, які можна безпечно передавати назад - тоді ваші основні об’єкти даних або потік можуть бути утилізовані using. Вам не потрібно писати дірявий код.
ToolmakerSteve

6

Це чудово - finallyречення (саме це робить фінальна фігурна дужка usingречення під капотом) завжди виконуються, коли область залишається, незалежно від того, як.

Однак це справедливо лише для тверджень, які знаходяться в блоці нарешті (які не можна явно встановити при використанні using). Тому у вашому прикладі scope.Complete()ніколи не викликатимуть (я сподіваюсь, компілятор попередить вас про недосяжний код).


2

Загалом, це хороший підхід. Але у вашому випадку, якщо ви повернетесь перед тим scope.Complete(), як зателефонувати , він просто смітить TransactionScope. Залежить від вашого дизайну.

Отже, у цьому прикладі Complete () не викликається, а область дії розподілена, припускаючи, що вона успадковує інтерфейс IDisposable.


Він повинен реалізовувати IDisposable або використовувати компіляцію звичок.
Chriseyre2000,

2

scope.Complete слід однозначно викликати раніше return. Компілятор відобразить попередження, і цей код ніколи не буде викликаний.

Що стосується returnсамого себе - так, можна з упевненістю назвати це внутрішньою usingзаявою. Використання перекладено для спроби остаточно блокувати за кадром, і нарешті блок, безумовно, повинен бути виконаний.


1

У наведеному вами прикладі є проблема; scope.Complete()ніколи не називається. По-друге, не є доброю практикою використовувати returnтвердження всередині usingвисловлювань. Зверніться до наступного:

using(var scope = new TransactionScope())
{
    //have some logic here
    return scope;      
}

У цьому простому прикладі справа в тому, що; Значення scopeбуде нульовим, коли використання оператора закінчено.

Тому краще не повертатися всередину, використовуючи оператори.


1
Тільки тому, що `` область повернення '' безглузда, це не означає, що оператор return є неправильним.
Preet Sangha

те, що якщо не використовувати найкращі практики, це не означає, що ви зробили щось неправильно. Це означає, що краще уникати, оскільки це може призвести до непередбачених наслідків.
daryal

1
Значення scopeНЕ буде нульовим - єдине , що буде сталося, що Dispose()буде були випробувані на цьому примірнику, і тому екземпляр повинен не використовувати більше (але не дорівнює нулю , і немає нічого попередження вам спробувати і використовувати утилізованого предмета, навіть якщо це справді є недоречним використанням одноразового предмета).
Lucero

Люсеро цілком правильний. Одноразові предмети не мають нульового значення після утилізації. Його властивість IsDisposed є істинним, але якщо ви перевірите проти null, ви отримуєте false і return scopeповертаєте посилання на цей об'єкт. Таким чином, якщо ви призначаєте це посилання після повернення, ви забороняєте GC очищати утилізований предмет.
ThunderGr

1

Щоб переконатися, що scope.Complete()буде викликано, оберніть його try/finally. disposeНазиваються , тому що ви повинні обернути його з , usingщо є альтернативою try/finallyблоку.

using(var scope = new TransactionScope())
{
  try
  {
  // my core logic
  return true; // if condition met else
  return false;
  }
  finally
  {
   scope.Complete();
  }
}

Я думаю, ти хочеш сказати, якщо ти виграв - якщо хочеш, а не буде ... відповідно до твого коду. :)

0

У цьому прикладі область scope.Complete () ніколи не буде виконана. Однак команда return очистить все, що призначено в стеку. GC подбає про все, що не є посиланням. Отже, якщо немає об’єкта, який не може бути підхоплений ГК, немає ніяких проблем.

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