处置或不处置 (CA2000)

发布于 2024-10-04 07:26:45 字数 1461 浏览 3 评论 0原文

我正在旧项目上启用代码分析。大多数评论我都能理解,但是CA2000:在失去范围之前释放对象 很难做对。

例如,来自 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”的所有引用超出范围之前,对对象“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 消息更改为“未沿所有异常路径进行处理” '

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.

For instance, this code from an ASP.Net page:

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:

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 : In method 'BuildTable()', object 'td' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'td' before all references to it are out of scope.
(and similar messages about the controls that are added to that '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'. 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'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

深者入戏 2024-10-11 07:26:45

代码分析无法完全理解您的代码,并且仅在您创建似乎未处理的一次性对象时发出警告。在您的情况下,您应该关闭警告,因为在离开方法之前不应释放该对象。您可以通过自定义代码分析规则集来关闭整个项目的警告,也可以在明显表明代码分析错误的每个具有此警告的方法上关闭警告。

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

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

除了这段代码是无意义的,因为您不想处置该行并且您刚刚添加到表格中的单元格。

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:

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.

顾北清歌寒 2024-10-11 07:26:45

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

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

由于上述两种情况在普通代码中都很常见,因此我认为 CA2000 规则对大多数代码库没有价值 - 有如此多的误报当这是一个真正的问题时,很可能有五十分之一的情况会被遗漏。

I think you have just shown that the CA2000 rule is not very useful on most code bases
As far as I know,

  • Dispose on HtmlTableRow does nothing useful unless it is being used inside of a UI designer; I have never seen anyone call dispose on the Asp.net controls. (Winforms/WPF is a different case)
  • You store the reference to td inside the table, so you should not dispose it anyway.

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.

软糖 2024-10-11 07:26:45

这段代码将消除这两个警告
(我使用 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;
            }
        }
    }

this code will get rid of both warnings
(I use a using(HtmlTable) to simulate your global HtmlTable member...):

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;
            }
        }
    }
街道布景 2024-10-11 07:26:45

创建控件后、对控件执行任何操作之前,立即将控件添加到集合中。

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。

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. 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.

霊感 2024-10-11 07:26:45

如果您认为代码分析是错误的(发生在我身上,它要求为未实现 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.
}

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.

[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.
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文