从内部使用块返回是否可以

时间:2011-04-06 14:30:31

标签: c# return

我正在进行代码审查,并找到了大量具有以下格式的代码:

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'未沿所有异常路径放置。在对所有引用超出范围之前,调用System.IDisposable.Dispose对象'MyResponse'。

5 个答案:

答案 0 :(得分:15)

不,没关系。

无论您放置finally的位置,using语句隐式生成的用于处理处理的return块都将执行。

您确定CA2000与myTracer而非abc相关吗?我猜这个警告正在发生,因为MyResponse实现了IDisposable并且您在返回之前没有处置abc。 (无论哪种方式,建议的重写都不会对警告产生任何影响。)

答案 1 :(得分:11)

您的重写不会修复CA2000警告,因为问题不是Tracer对象,而是MyResponse对象。
The documentation州:

  

以下是使用声明不足以保护IDisposable对象并可能导致CA2000发生的一些情况。
  返回一次性对象需要在使用块之外的try / finally块中构造对象。

要修复警告without messing with the stack trace of your exceptions(< - click,这是一个链接),请使用以下代码:

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;
}

为什么呢?请参阅链接文档中的示例。

答案 2 :(得分:4)

警告可能约为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 内部显式抛出的异常,并使第一个堆栈帧的行号突变为指向throw;声明。

实际上,这意味着如果throw内有多个MyResponse语句,并且它们使用相同的消息抛出相同类型的异常,您将无法分辨哪个throw当你发现异常时,它正是负责任的。

这是恕我直言,这是一个纯粹的学术问题,但我提到它是完整的。

答案 3 :(得分:2)

这并不重要。但是,与@Aliostad相反,我认为版本2,return块之外的using是更好的风格。

我的理由是这样的:

using块表示“打开”和“关闭”的内容。这是一种令人讨厌的交易。关闭using块说我们已经完成了我们的工作,现在继续使用return等其他内容是安全的。

答案 4 :(得分:0)

此警告可能与“单一退出点”的原则有关。这里讨论:http://c2.com/cgi/wiki?SingleFunctionExitPoint