从using block内部返回是可以的吗?
本文关键字:using block 内部 返回 | 更新日期: 2023-09-27 17:49:30
我正在做代码审查,并发现许多代码具有以下格式:
public MyResponse MyMethod(string arg)
{
using (Tracer myTracer = new Tracer(Constants.TraceLog))
{
MyResponse abc = new MyResponse();
// Some code
return abc;
}
}
当我运行代码分析时,我得到CA2000警告Microsoft。可靠性
代码应该重写为:
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。可靠性:在方法'xxxxx(Guid, Guid)'中,对象'MyResponse '不会沿着所有异常路径进行处理。在对象'MyResponse '的所有引用都超出作用域之前,调用System.IDisposable.Dispose。
不,没关系。
using
语句隐式生成的finally
块将执行,无论你把return
放在哪里。
你确定CA2000涉及myTracer
而不是abc
吗?我猜发生警告是因为MyResponse
实现了IDisposable
,而您在返回之前没有处理abc
。(无论哪种方式,您建议的重写都不会对警告产生影响。)
您的重写将无法修复CA2000警告,因为问题不是Tracer
对象,而是MyResponse
对象。
文档说明:
要修复警告而不干扰异常的堆栈跟踪(<-单击,这是一个链接),使用以下代码:以下是using语句不足以保护可丢弃对象并可能导致CA2000发生的一些情况。
返回一个一次性对象要求该对象是在using块之外的try/finally块中构造的。
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;
}
为什么?
这个警告可能是关于MyResponse
的,也就是IDisposable
。
为什么会出现警告?
如果构造了一个MyResponse
对象,但是方法后面的代码导致抛出异常,那么对该对象的所有引用都将丢失(我们只有一个,并且没有设法返回它)。这意味着不能再在对象上调用Dispose
,我们将依靠类终结器来清理所有资源。
有关系吗?
一般来说,只有当:
-
IDisposable
封装了可能被程序的其他部分或另一个进程"很快"需要的资源 - 在方法返回之前抛出异常,触发"problem"
- 资源没有被终结器及时释放,或由于某种原因,终结器从未运行,但你的应用程序没有下降
所以,不,这并不重要。
如何修复?
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
内部显式抛出的异常将被重新抛出,并将第一个堆栈帧的行号改变为指向throw;
语句。
实际上,这意味着如果在MyResponse
中有多个throw
语句,并且它们抛出具有相同消息的相同类型的异常,那么当您捕获异常时,您将无法准确地告诉哪个throw
负责。
在我看来这是一个纯粹的学术问题,但我提到它是为了完整。
这并不重要。但是,与@Aliostad相反,我认为版本2,在using
块之外的return
是更好的风格。
我的理由是这样的:
using
块表示"打开"answers"关闭"的东西。这是一种廉价的交易。关闭using
块表示我们已经完成了我们的工作,现在可以继续处理其他东西了,比如return
。
此警告可能与"单出口点"原则有关。这里讨论:http://c2.com/cgi/wiki?SingleFunctionExitPoint