代码之家  ›  专栏  ›  技术社区  ›  Hans Kesting

是否处置(CA2000)

  •  13
  • Hans Kesting  · 技术社区  · 14 年前

    我正在打开一个旧项目的代码分析。大多数的评论我都能理解,但是 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 回复  |  直到 14 年前
        1
  •  10
  •   Martin Liversage    12 年前

    代码分析无法完全理解您的代码,如果您创建了一个似乎无法释放的可释放对象,它只会发出警告。在您的情况下,应该关闭警告,因为在离开方法之前不应该释放对象。您可以通过自定义代码分析规则集来关闭整个项目的警告,也可以在每个具有此警告的方法(很明显代码分析是错误的)上关闭警告。

    也就是说,我建议您使用 using 处理时构造 IDisposable 物体:

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

    不过,这段代码毫无意义,因为您不想丢弃刚刚添加到表中的行和单元格。

        2
  •  2
  •   Ian Ringrose    14 年前

    我认为你刚刚表明CA2000规则在 代码基 据我所知,

    • HtmlTableRow上的Dispose没有任何用处,除非它是在UI设计器中使用的;我从未见过有人对Asp.net控件调用Dispose。(Winforms/WPF是另一种情况)
    • 您将对td的引用存储在表中,因此无论如何都不应该处理它。

    由于以上两种情况在正常代码中都很常见,我认为CA2000规则对 有代码基础 这么多误报 当这是一个真正的问题时,你很可能会错过每50个案例中的1个。

        3
  •  2
  •   eFloh    13 年前

    此代码将消除这两个警告 (我使用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;
                }
            }
        }
    
        4
  •  1
  •   jrr    14 年前

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

    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了。

        5
  •  0
  •   mathifonseca Piyush Chaudhari    12 年前

    如果您认为代码分析是错误的(碰巧它要求为未实现IDisposable的对象调用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.
    }