处置或不处理(CA2000)

时间:2010-11-24 09:02:41

标签: .net dispose code-analysis

我正在开启旧项目的代码分析。大多数评论都是我能理解的结果,但CA2000: Dispose objects before losing scope很难做到。

例如,来自ASP.Net页面的代码:

private void BuildTable()
{
    HtmlTableRow tr = new HtmlTableRow();
    HtmlTableCell td = new HtmlTableCell();

    tr.Cells.Add(td);
    // add some controls to 'td'

    theTable.Rows.Insert(0, tr);
    // 'theTable' is an HtmlTable control on the page
}

提供CA消息:

  

CA2000:Microsoft.Reliability:在方法'BuildTable()'中,在对所有引用超出范围之前,在对象'tr'上调用System.IDisposable.Dispose。

     

CA2000:Microsoft.Reliability:在方法'BuildTable()'中,对象'td'未沿所有异常路径放置。在对对象'td'的所有引用都超出范围之前调用System.IDisposable.Dispose。   (以及关于添加到'td'的控件的类似消息。)

我可以解决第二个问题:

private void BuildTable()
{
    HtmlTableRow tr = new HtmlTableRow();
    HtmlTableCell td = new HtmlTableCell();

    try
    {
        tr.Cells.Add(td);
        // add some controls to 'td'

        td = null; // this line is only reached when there were no exceptions
    }
    finally
    {
        // only dispose if there were problems ('exception path')
        if (td != null) td.Dispose();
    }

    theTable.Rows.Insert(0, tr);
}

但我不认为有可能解决有关'tr'的消息。我无法处理,因为在方法退出后仍然需要它。

或者我错过了什么?

顺便说一句:将theTable.Rows.Insert更改为theTable.Rows.Add会将CA邮件更改为“未按所有例外路径排列”

5 个答案:

答案 0 :(得分:10)

代码分析无法完全理解您的代码,只是在您创建一个似乎没有处置的一次性对象时会发出警告。在您的情况下,您应该关闭警告,因为在离开方法之前不应该丢弃对象。您可以通过自定义代码分析规则集或在具有此警告的每个方法上显示代码分析错误,从而关闭整个项目的警告。

那就是说,我建议您在处理using个对象时使用IDisposable构造:

using (var tr = new HtmlTableRow()) {
  using (var td = new HtmlTableCell()) {
    tr.Cells.Add(td);
    theTable.Rows.Insert(0, tr);
  }
}

除非此代码无意义,因为您不想处置刚添加到表格中的行和单元格。

答案 1 :(得分:2)

我认为您刚刚证明CA2000规则在大多数代码库中不是很有用 据我所知,

  • Dispose on HtmlTableRow除了在UI设计器中使用之外没有任何用处;我从未见过有人在Asp.net控件上调用dispose。 (Winforms / WPF是一个不同的案例)
  • 您将对td的引用存储在表中,因此无论如何都不应该将其丢弃。

由于以上两种情况在普通代码中都很常见,我没有看到CA2000规则对大多数代码库有价值 - 有这么多误报当你遇到真正的问题时,你很可能会错过50个案例中的1个。

答案 2 :(得分:2)

此代码将摆脱两个警告 (我使用using(HtmlTable)来模拟你的全局HtmlTable成员......):

using (HtmlTable theTable = new HtmlTable())
{
    HtmlTableRow tr = null;
    try
    {
        HtmlTableCell td = null;

        try
        {
            td = new HtmlTableCell();

            // add some controls to 'td'


            tr = new HtmlTableRow();
            tr.Cells.Add(td);

            /* td will now be disposed by tr.Dispose() */
            td = null;
        }
        finally
        {
            if (td != null)
            {
                td.Dispose();
                td = null;
            }
        }

        theTable.Rows.Insert(0, tr);

        /* tr will now be disposed by theTable.Dispose() */
        tr = null;
    }
    finally
    {
        if (tr != null)
        {
            tr.Dispose();
            tr = null;
        }
    }
}

但我认为你会考虑使用一种使用子功能的方法来使代码更清晰:

    private static void createTable()
    {
        using (HtmlTable theTable = new HtmlTable())
        {
            createRows(theTable);
        }
    }

    private static void createRows(HtmlTable theTable)
    {
        HtmlTableRow tr = null;
        try
        {
            tr = new HtmlTableRow();
            createCells(tr);

            theTable.Rows.Insert(0, tr);

            /* tr will now be disposed by theTable.Dispose() */
            tr = null;
        }
        finally
        {
            if (tr != null)
            {
                tr.Dispose();
                tr = null;
            }
        }
    }

    private static void createCells(HtmlTableRow tr)
    {
        HtmlTableCell td = null;

        try
        {
            td = new HtmlTableCell();

            // add some controls to 'td'


            tr.Cells.Add(td);

            /* td will now be disposed by tr.Dispose() */
            td = null;
        }
        finally
        {
            if (td != null)
            {
                td.Dispose();
                td = null;
            }
        }
    }

答案 3 :(得分:1)

在创建后直接将控件添加到集合中,但在对控件执行任何操作之前。

HtmlTableRow tr = new HtmlTableRow();
theTable.Rows.Insert(0, tr);

HtmlTableCell td = new HtmlTableCell();
tr.Cells.Add(td);

// add some controls to 'td'

由于在创建和添加/插入控件到集合之间不会有异常,因此不需要try / catch。将控件添加到集合后发生异常时,页面将丢弃它。您不会以这种方式获得CA2000。

答案 4 :(得分:0)

如果您认为代码分析错误(我发现它要求为没有实现IDisposable的对象调用Dispose)或者您认为没有必要Dispose该对象,您可以随时抑制这样的消息。

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000: DisposeObjectsBeforeLosingScope")]
public static IDataReader RetrieveData(string conn, string sql)
{
    SqlConnection connection = new SqlConnection(conn);
    SqlCommand command = new SqlCommand(sql, conn);
    return command.ExecuteReader(CommandBehavior.CloseConnection);
    //Oops, I forgot to dispose of the command, and now I don't get warned about that.
}