简体   繁体   English

处置或不处理(CA2000)

[英]To Dispose or not to Dispose (CA2000)

I'm switching on Code Analysis on an older project. 我正在开启旧项目的代码分析。 Most remarks that result I can understand, but the CA2000: Dispose objects before losing scope is hard to get right. 大多数评论结果我都能理解,但CA2000:在丢失范围之前处理对象很难做到。

For instance, this code from an ASP.Net page: 例如,来自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
}

Gives CA messages: 提供CA消息:

CA2000 : Microsoft.Reliability : In method 'BuildTable()', call System.IDisposable.Dispose on object 'tr' before all references to it are out of scope. CA2000:Microsoft.Reliability:在方法'BuildTable()'中,在对所有引用超出范围之前,在对象'tr'上调用System.IDisposable.Dispose。

CA2000 : Microsoft.Reliability : In method 'BuildTable()', object 'td' is not disposed along all exception paths. CA2000:Microsoft.Reliability:在方法'BuildTable()'中,对象'td'未沿所有异常路径放置。 Call System.IDisposable.Dispose on object 'td' before all references to it are out of scope. 在对对象'td'的所有引用都超出范围之前调用System.IDisposable.Dispose。 (and similar messages about the controls that are added to that 'td'.) (以及关于添加到'td'的控件的类似消息。)

I can resolve the second problem: 我可以解决第二个问题:

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

But I don't think it is possible to resolve the message about the 'tr'. 但我不认为有可能解决有关'tr'的消息。 I can't Dispose of that, because it's still needed after the method has exited. 我无法处理,因为在方法退出后仍然需要它。

Or did I miss something? 还是我错过了什么?

By the way: changing that theTable.Rows.Insert into theTable.Rows.Add changes the CA message to 'not disposed along all exception paths' 顺便说一句:改变这种theTable.Rows.InserttheTable.Rows.Add改变CA消息“沿着所有的异常路径没有设置”

The code analysis is unable to completely understand your code and simply warns if you create a disposable object that seems to not be disposed. 代码分析无法完全理解您的代码,只是警告您是否创建了似乎没有处置的一次性对象。 In your case you should turn off the warning because the object should not be disposed before leaving the method. 在您的情况下,您应该关闭警告,因为在离开方法之前不应该丢弃对象。 You can turn warnings off either for the entire project by customizing the code analysis rule set or on each method having this warning where it is obvious that the code analysis is wrong. 您可以通过自定义代码分析规则集或在具有此警告的每个方法上显示代码分析错误,从而关闭整个项目的警告。

That said, I recommend that you use the using construct when dealing with IDisposable objects: 也就是说,我建议您在处理IDisposable对象时使用using构造:

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

Except this code is nonsense as you don't want to dispose the row and cell you just added to the table. 除了这段代码是无稽之谈,因为你不想处理你刚刚添加到表中的行和单元格。

I think you have just shown that the CA2000 rule is not very useful on most code bases As far as I know, 我想您刚刚证明CA2000规则在大多数代码库中都不是很有用据我所知,

  • Dispose on HtmlTableRow does nothing useful unless it is being used inside of a UI designer; 除非在UI设计器中使用,否则HtmlTableRow上的Dispose没有任何用处; I have never seen anyone call dispose on the Asp.net controls. 我从未见过有人在Asp.net控件上调用dispose。 (Winforms/WPF is a different case) (Winforms / WPF是另一种情况)
  • You store the reference to td inside the table, so you should not dispose it anyway. 您将对td的引用存储在表中,因此无论如何都不应该将其丢弃。

As both of the above is very common in normal code, I don't see the CA2000 rule to be of value to most code bases – there are so many false positives you are very likely to miss in 1 in 50 cases when it is a real problem. 由于上述两种情况在普通代码中都非常常见,我没有看到CA2000规则对大多数代码库都有价值 - 在50个案例中,当你有很多误报时很可能会错过真正的问题。

this code will get rid of both warnings (I use a using(HtmlTable) to simulate your global HtmlTable member...): 这段代码将摆脱两个警告(我使用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;
        }
    }
}

but I think you will consider using an approach that uses subfunctions to make the code more clear: 但我认为你会考虑使用一种使用子功能的方法来使代码更清晰:

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

Add the control to the collection directly after creating it, but before you do anything with the control. 在创建控件后,直接将控件添加到集合中,但在对控件执行任何操作之前。

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

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

// add some controls to 'td'

As there can't be an exception between creating and adding/inserting the control to the collection there is no need for try/catch. 由于在创建和添加/插入控件到集合之间不会有异常,因此不需要try / catch。 When an exception occurs after the control is added to the collection the page will dispose of it. 将控件添加到集合后发生异常时,页面将丢弃它。 You won't get the CA2000 this way. 你不会这样得到CA2000。

In case you think that Code Analysis is wrong (happened to me that it asked to call Dispose for an object that didn't implement IDisposable) or you don't feel it is necessary to Dispose that object, you can always suppress that message like this. 如果您认为代码分析是错误的(我发现它要求为没有实现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.
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM