使用块从内部返回是不是可以
Posted
技术标签:
【中文标题】使用块从内部返回是不是可以【英文标题】:Is it OK doing a return from inside using block使用块从内部返回是否可以 【发布时间】:2011-07-30 21:30:27 【问题描述】:我正在做代码审查,发现很多代码格式如下:
public MyResponse MyMethod(string arg)
using (Tracer myTracer = new Tracer(Constants.TraceLog))
MyResponse abc = new MyResponse();
// Some code
return abc;
当我运行代码分析时,我收到 CA2000 警告 Microsoft.Reliability
代码是否应该改写为:
public MyResponse MyMethod(string arg)
MyResponse abc = new MyResponse();
using (Tracer myTracer = new Tracer(Constants.TraceLog))
// Some code
return abc;
还是没关系?
编辑
报告警告的行是:
MyResponse abc = new MyResponse();
MyResponse 是标准数据集。
完整的错误信息是:
警告 150 CA2000:Microsoft.Reliability:在方法“xxxxx(Guid, Guid)”中,对象“MyResponse”并未沿所有异常路径进行处理。在对对象“MyResponse”的所有引用超出范围之前调用 System.IDisposable.Dispose。
【问题讨论】:
是MyResponse
IDisposable
?
这个警告没有任何意义恕我直言......我看不出第二个版本会更好的任何理由
@Jon,不管是不是,MyResponse
不受using
的处置语义的约束。这不是这里的问题,除了返回的对象。
您是否出于某种原因将警告文本保密?我们没有精神力量;如果您想诊断错误消息发布消息。
@Eric:通常情况下,我会同意你的看法,但 CA2000 和他的例子很清楚,你不这么认为吗?
【参考方案1】:
不,没关系。
using
语句隐式生成的用于处理处置的finally
块将执行,无论您将return
放在何处。
您确定 CA2000 与 myTracer
而不是 abc
相关吗?我猜想警告正在发生,因为MyResponse
实现了IDisposable
,并且您在返回之前没有处理abc
。 (无论哪种方式,您建议的重写应该对警告没有影响。)
【讨论】:
@smartcaveman - 问题以“或者这无关紧要?”的问题结束。所以这个答案开头的陈述非常好。 @Richard,我没说错。我说这在标题的上下文中令人困惑。 我不想挑剔,但我不明白为什么这个答案得到了这么多的支持。第一部分与提供的警告无关,第二部分没有提供解决方案... @Daniel:OP 询问“是否可以从 using 块内部返回”和“是否应该重写代码以将返回移动到 using 块之外”。我正在按照要求回答这些问题,然后推测问题的真正根本原因(推测是因为 OP 没有提供足够的具体细节)。您自己的答案为(假定的)实际问题提供了解决方案,如果您认为值得,我很乐意在这里重现类似的内容。【参考方案2】:您的重写不会修复 CA2000 警告,因为问题不是 Tracer
对象,而是 MyResponse
对象。The documentation 声明:
以下是 using 语句不足以保护 IDisposable 对象并可能导致 CA2000 发生的一些情况。 返回一次性对象需要在 using 块之外的 try/finally 块中构造该对象。
要修复警告without messing with the stack trace of your exceptions(
public MyResponse MyMethod(string arg)
MyResponse tmpResponse = null;
MyResponse response = null;
try
tmpResponse = new MyResponse();
using (Tracer myTracer = new Tracer(Constants.TraceLog))
// Some code
response = tmpResponse;
tmpResponse = null;
finally
if(tmpResponse != null)
tmpResponse .Dispose();
return response;
为什么?请参阅链接文档中的示例。
【讨论】:
我认为try/catch
可能更可取,因为它不需要您创建临时变量——这里的治疗方法可能比疾病更糟糕。
这完全取决于你的情况,如果你想实现 something 来修复这个警告。如果不经常调用该方法,我不会因为我不认为这是一个问题。但是,我不同意使用try/catch
,因为它可能会混淆异常的堆栈跟踪(请参阅答案中的链接)。
顺便说一句:我提供的修复是MS官方建议修复此警告。
@DanielHilgarth:编程中没有“力量”; throw;
不会弄乱堆栈跟踪,仅此而已。至于官方的建议,我认为作为专业人士,我们不应该盲目“听从命令”。我们可以并且应该独立判断每种方法的优点。无论如何,我并不是在这里提倡一种真正的解决方案。只是说如果可读性对你和我一样重要,try/catch
可能会更好。
@Jon: 请阅读提供的链接,如果异常以与 try/catch 相同的方法抛出,则 确实 弄乱堆栈跟踪!关于盲目听从命令:我完全同意,但提供的解决方案是解决问题没有副作用的方式。【参考方案3】:
警告可能是关于MyResponse
,也就是IDisposable
。
为什么会出现警告?
如果构造了一个MyResponse
对象,但该方法后面的代码导致抛出异常,则对该对象的所有引用都将丢失(我们只有一个,并没有设法返回它)。这意味着不能再对对象调用Dispose
,我们将依赖类终结器来清理任何资源。
重要吗?
一般来说,只有在以下情况下才重要:
IDisposable
封装了程序的其他部分或其他进程可能“很快”需要的资源
方法返回前抛出异常,触发“问题”
终结器没有尽快释放该资源,或由于某种原因,终结器永远不会运行,但您的应用程序不会关闭
所以不,这应该不重要。
如何解决?
public MyResponse MyMethod(string arg)
MyResponse abc = null;
try
abc = new MyResponse();
using (Tracer myTracer = new Tracer(Constants.TraceLog))
// Some code
return abc;
catch
if (abc != null)
abc.Dispose();
throw;
这可确保如果控件通过异常退出方法,abc
要么是 null
,要么已被正确处理。
更新
事实证明,当使用这种处理方式时,从 MyMethod
内部显式抛出的异常将被重新抛出,并且第一个堆栈帧的行号会更改为指向 @987654331 @ 声明。
实际上,这意味着如果您在 MyResponse
内有多个 throw
语句,并且它们以相同的消息抛出相同类型的异常,那么当您捕获时,您将无法准确判断是哪个 throw
负责例外。
恕我直言,这是一个纯粹的学术问题,但为了完整起见,我提到它。
【讨论】:
我总是会处理 IDisposable 对象。不能保证 IDisposable 类甚至有终结器。 @TrueWill:所以你会在你的代码中禁止任何返回IDisposable
的方法?那是不是有点过分了?这是该策略的一个受害者:msdn.microsoft.com/en-us/library/b9skfh7s.aspx。一次性物品应该被丢弃,当然,但只有在我们用完之后。
我不会禁止这样做。方法可以返回一个 IDisposable,但每个一次性实例都必须有一个所有者(从某种意义上说是负责处置它的东西)。方法契约(可能是 XML cmets)应该指定调用者是否负责处理返回的实例。
顺便说一句:您可以完全跳过 abc=null
分配和 if(abc!=null)
检查,只需将 abc = new MyResponse();
放在 try 块之前 - 无需任何更改(既不会丢失异常也不会丢弃实例)。
【参考方案4】:
这并不重要。但是,与@Aliostad 不同,我认为在using
块之外使用return
的版本2 更好。
我的理由是这样的:
using
块表示“打开”和“关闭”的东西。这是一种廉价交易。关闭using
块表示我们已经完成了我们的工作,现在可以安全地继续处理其他内容,例如return
ing。
【讨论】:
【参考方案5】:这个警告很可能与“单一出口点”的原则有关。这里讨论:http://c2.com/cgi/wiki?SingleFunctionExitPoint
【讨论】:
以上是关于使用块从内部返回是不是可以的主要内容,如果未能解决你的问题,请参考以下文章
为啥代理下的getContextPath()返回的是HttpServlet内部的内部路径,而不是Filter内部的路径?
Lua 函数如何返回 nil,即使函数内部的返回值不是 nil?
在windows系统下的cmd下面执行php 命令,返回‘PHP’不是内部或外部命令,也不是可运行的程序。。