从内部使用块返回是否可以
我正在进行代码审查,并找到了许多具有以下格式的代码:
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’未沿所有exception路径放置。 对所有对它的引用超出范围之前,在对象’MyResponse’上调用System.IDisposable.Dispose。
您的重写不会修复CA2000警告,因为问题不是Tracer
对象,而是MyResponse
对象。
文件说明:
以下是使用语句不足以保护IDisposable对象并可能导致CA2000发生的一些情况。
返回一次性对象需要在使用块之外的try / finally块中构造对象。
要修复警告而不弄乱exception的堆栈跟踪 (< - 单击,这是一个链接),请使用以下代码:
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; }
为什么? 请参阅链接文档中的示例。
不,没关系。
无论您将return
放在何处,由using
语句隐式生成以处理处理的finally
块都将执行。
你确定CA2000与myTracer
而不是abc
吗? 我猜这个警告正在发生,因为MyResponse
实现了IDisposable
并且你在返回之前没有处理abc
。 (无论哪种方式,建议的重写都不会对警告产生任何影响。)
警告可能是关于MyResponse
,这是IDisposable
。
为什么出现警告?
如果构造了一个MyResponse
对象,但是稍后在该方法中的代码会导致抛出exception,那么对该对象的所有引用都将丢失(我们只有一个,并且没有设法返回它)。 这意味着不能再在对象上调用Dispose
,我们将依赖类终结器来清理任何资源。
有关系吗?
一般来说,只有在以下情况下才有意义:
-
IDisposable
封装了程序的其他部分或其他进程“很快”可能需要的资源 - 在方法返回之前抛出exception,以触发“问题”
- 终结器不会很快释放该资源, 或者由于某种原因,终结器永远不会运行,但您的应用程序不会停止运行
所以不,它应该不重要。
怎么解决?
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; } }
这确保了如果控制通过exception退出方法,则abc
为null
或已正确处理。
更新
事实certificate,当使用这种处理方式时,将重新抛出从MyMethod
内部显式抛出的exception,并使第一个堆栈帧的行号突变为指向throw;
声明。
实际上这意味着如果你在MyResponse
有多个throw
语句,并且它们使用相同的消息抛出相同类型的exception,那么当你捕获exception时,你将无法确定哪个throw
是完全负责的。
这是恕我直言,这是一个纯粹的学术问题,但我提到它是完整的。
这并不重要。 但是,与@Aliostad相反,我认为版本2,在using
块之外的return
是更好的风格。
我的理由是这样的:
using
块表示“打开”和“关闭”的内容。 这是一种令人讨厌的交易。 关闭using
块说我们已经完成了我们的工作,现在继续使用其他东西是安全的,比如return
。
这个警告可能与“单一退出点”原则有关。 这里讨论: http : //c2.com/cgi/wiki?SingleFunctionExitPoint