使用{}语句在内部调用return是一个好方法吗?


93

我只想知道returnusing块内调用的安全/良好方法。

对于前。

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将“破坏”范围-因此这些对象将按预期处置
Shai 2012年

4
请注意,您scope.Complete()提供的示例将永远不会打您的电话,因此您的交易将始终回滚。
安迪

不管usingdispose()叫,当您返回时,包含此功能using块将已返回和属于它的一切将被孤立。因此,即使scope没有被“处置” using(正如其他人所解释的那样),它也将被处置,因为功能已结束。如果C#有goto声明-您笑了吗?好-然后您可以goto在右括号后返回,而不用返回,而无需返回。从逻辑上讲,它scope仍然会被处理掉,但是您只是使用goto了C#,所以谁在此阶段关心逻辑。
极好的


Answers:


145

return在您的using块内调用是绝对安全的,因为using块只是一个try/finally块。

在上面的示例中,return之后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
康拉德·鲁道夫2012年

可以将use块留给return可以,但是在使用TransactionScope时,您可能会在使用语句本身上遇到麻烦:blogs.msdn.com/b/florinlazar/archive/2008/05/05/8459994.aspx
thewhiteambit 2015年

以我的经验,这不适用于SQL Server CLR程序集。当我需要返回包含引用MemoryStream对象的SqlXml字段的UDF的结果时。我收到“无法访问已处置的对象”和“在关闭流时调用Read的无效尝试。 ”,所以在这种情况下,我被迫编写泄漏代码并放弃使用状态。:(我唯一的希望是SQL CLR将处理这些对象的处理。这是一个独特的场景,但我想与我分享
。– MikeTeeVee

1
@MikeTeeVee-清洁器解决方案是(a)让调用者执行using,例如using (var callersVar = MyFunc(..)) ..而不是在“ MyFunc”内部使用-我的意思是为调用者提供了流并负责通过using或显式关闭它,或(b)让MyFunc将所需的任何信息提取到其他对象中,然后可以安全地传递回去-然后,底层数据对象或流可以由处置using。您不必编写泄漏代码。
ToolmakerSteve

6

很好-finally子句(这是子句的大括号在using后台执行的操作)总是在离开范围时执行,无论如何。

但是,这仅适用于finally块中的语句(使用时不能显式设置using)。因此,在您的示例中,scope.Complete()将永远不会调用(我希望编译器会警告您有关无法访问的代码)。


2

通常,这是一个好方法。但就您而言,如果您在调用之前返回scope.Complete(),则只会破坏TransactionScope。取决于您的设计。

因此,在此示例中,假设它继承了IDisposable接口,则不调用Complete(),而是处理范围。


它必须实现IDisposable或使用不会编译。
Chriseyre2000

2

scope.Complete应该绝对在调用之前return。编译器将显示警告,并且永远不会调用此代码。

关于return自身-是的,可以在using语句内部调用它。将using转换为在后台尝试最终阻止,并最终确定执行该阻止。


1

在您提供的示例中,有一个问题。scope.Complete()从未被调用。其次,在return语句内部使用语句不是一个好习惯using。请参阅以下内容:

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

在这个简单的例子中,重点是:的价值scopeusing语句完成时,将为null。

因此最好不要在using语句内返回。


1
仅仅因为'return scope'是没有意义的,并不意味着return语句是错误的。
Preet Sangha

仅仅因为不使用最佳实践并不意味着您做错了事。这意味着最好避免,因为它可能导致无法预料的后果。
daryal

1
的值scope将不会为null-将会发生的唯一事情是将Dispose()在该实例上调用该实例,因此不应再使用该实例(但它不能为null,也不会阻止您尝试使用即使确实不当使用一次性物品)。
Lucero

Lucero是非常正确的。一次性对象处置后不为null。它的IsDisposed属性为true,但是如果检查为null,则为false,并return scope返回对该对象的引用。这样,如果您在返回时分配该引用,则可以防止GC清理已处置的对象。
ThunderGr

1

要确保scope.Complete()将调用,请用将其包装try/finallydispose之所以称为,是因为您用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将处理所有未引用的内容。因此,除非存在GC无法拾取的对象,否则不会有问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.