Это хороший подход - вызвать 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, dispose()вызывается ли, когда вы вернетесь, функция, содержащая этот usingблок, будет возвращена, и все, что ей принадлежит, станет бесхозным. Таким образом, даже если scopeон не был удален «самим using» (он будет, как объяснили другие), он все равно будет удален, потому что функция завершилась. Если бы в C # был gotoоператор - вы еще не смеялись? хорошо - тогда вместо возврата вы можете gotoперейти после закрывающей скобки, не возвращаясь. Логично, scopeчто все равно будет удалено, но вы только что добавили gotoC #, так что кого волнует логика на этом этапе.
Superbest
В C # есть goto .
Джейк Т.

Ответы:

146

Совершенно безопасно вызывать returnвнутри вашего usingблока, поскольку используемый блок - это просто try/finallyблок.

В приведенном выше примере после возврата trueобласть видимости будет удалена и значение будет возвращено. return falseИ scope.Complete()будет не дозвонились. Disposeоднако будет вызываться независимо от того, что находится внутри блока finally.

Ваш код по сути такой же (если это упрощает понимание):

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 вас перезвонят. Если OP не знает, что происходит using, скорее всего, он не знает, что происходит с finally.
Конрад Рудольф
Можно оставить блок using с возвратом, но в случае TransactionScope у вас могут возникнуть проблемы с самим оператором using
thewhiteambit
По моему опыту, это не работает со сборками SQL Server CLR. Когда мне нужно вернуть результаты для UDF, содержащего поле SqlXml, которое ссылается на объект MemoryStream. Я получаю сообщения « Невозможно получить доступ к удаленному объекту » и « Недопустимая попытка вызвать Read, когда поток закрыт », поэтому меня ПРИНУДИТЕЛЬНО написать негерметичный код и отказаться от использования оператора в этом сценарии. :( Я надеюсь, что SQL CLR справится с удалением этих объектов. Это уникальный сценарий, но я подумал, что поделюсь им.
MikeTeeVee
1
@MikeTeeVee - Решения для чистки: либо (а) вызывающий выполняет using, например using (var callersVar = MyFunc(..)) .., вместо использования внутри "MyFunc" - я имею в виду, что вызывающему абоненту предоставляется поток и он отвечает за его закрытие через usingили явно, либо (б) пусть MyFunc извлечет всю необходимую информацию в другие объекты, которые можно безопасно передать обратно - тогда базовые объекты данных или поток могут быть удалены вашим using. Вам не нужно писать негерметичный код.
ToolmakerSteve
7

Это нормально - finallyпредложения (что и делает закрывающая фигурная скобка usingпредложения под капотом) всегда выполняются при выходе из области видимости, независимо от того, как.

Однако это верно только для операторов, которые находятся в блоке finally (который не может быть явно установлен при использовании using). Следовательно, в вашем примере scope.Complete()он никогда не будет вызван (хотя я ожидаю, что компилятор предупредит вас о недоступности кода).

Lucero
источник
2

В общем, подход хороший. Но в вашем случае, если вы вернетесь до вызова scope.Complete(), он просто уничтожит TransactionScope. Зависит от вашего дизайна.

Итак, в этом примере Complete () не вызывается, а область видимости удаляется, предполагая, что она наследует интерфейс IDisposable.

М. Меннан Кара
источник
Он должен реализовывать IDisposable или использовать wont compile.
Chriseyre2000 02
2

scope.Complete обязательно должен вызываться раньше return. Компилятор выдаст предупреждение, и этот код никогда не будет вызван.

Что касается returnсамого себя - да, это смело можно назвать внутренним usingутверждением. Использование переводится как блок try-finally за сценой, и блок finally обязательно должен быть выполнен.

Тони Х
источник
1

В приведенном вами примере есть проблема; scope.Complete()никогда не называется. Во-вторых, использование returnоператоров внутри usingоператоров - не лучшая практика . См. Следующее:

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

В этом простом примере суть в том, что; значение scopeбудет нулевым, когда оператор using будет завершен.

Так что лучше не возвращаться внутрь с помощью операторов.

Дарьял
источник
1
Тот факт, что «область возврата» бессмысленна, это не означает, что оператор возврата неверен.
Preet Sangha
просто потому, что неиспользование передового опыта не означает, что вы сделали что-то неправильно. Подразумевается, что лучше избегать, так как это может привести к непредвиденным последствиям.
Дарьял 02
1
Значение scopeне будет нулевым - единственное , что будет произошло, что Dispose()будет были испробованы на этом экземпляре, и поэтому экземпляр должен не использовать больше (но не равно нулю , и нет ничего предупреждения вам попробовать и использовать удаленный объект, даже если это действительно неправильное использование одноразового объекта).
Lucero
Лусеро совершенно прав. Одноразовые объекты не имеют значения NULL после удаления. Его свойство IsDisposed имеет значение true, но если вы проверите значение null, вы получите false и return scopeвернете ссылку на этот объект. Таким образом, если вы назначаете эту ссылку при возврате, вы не позволяете GC очищать удаленный объект.
ThunderGr 05
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 позаботится обо всем, на что нет ссылок. Итак, если нет объекта, который не может быть захвачен сборщиком мусора, проблем нет.

ThunderGr
источник