Утилизировать или не утилизировать (CA2000) - программирование
Подтвердить что ты не робот

Утилизировать или не утилизировать (CA2000)

Я использую Code Analysis для более старого проекта. Большинство замечаний, которые я могу понять, но 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()" вызовите System.IDisposable. Задайте объект "tr", прежде чем все ссылки на него выйдут из области видимости.

CA2000: Microsoft.Reliability: в методе 'BuildTable()' объект 'td' не расположен по всем путям исключений. Вызовите System.IDisposable.Dispose на объекте 'td', прежде чем все ссылки на него выйдут из области видимости. (и аналогичные сообщения об элементах управления, которые добавлены к этому "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 на "не удаляется по всем путям исключений"

4b9b3361

Ответ 1

Анализ кода не может полностью понять ваш код и просто предупреждает, если вы создаете одноразовый объект, который, кажется, не удаляется. В вашем случае вы должны отключить предупреждение, потому что объект не должен быть удален до выхода из метода. Вы можете отключить предупреждения для всего проекта, настроив набор правил анализа кода или каждый метод, имеющий это предупреждение, где очевидно, что анализ кода неверен.

Тем не менее, я рекомендую использовать конструкцию using при работе с объектами IDisposable:

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

Кроме этого кода вздор, поскольку вы не хотите удалять строку и ячейку, которые вы только что добавили в таблицу.

Ответ 2

Я думаю, вы только что показали, что правило CA2000 не очень полезно для большинства кодовых баз Насколько я знаю,

  • Dispose on HtmlTableRow не делает ничего полезного, если он не используется внутри дизайнера пользовательского интерфейса; Я никогда не видел, чтобы кто-либо обращался к управлению Asp.net. (Winforms/WPF - это другой случай)
  • Вы сохраняете ссылку на td внутри таблицы, поэтому вы не должны ее уничтожать.

Как и вышеописанное очень часто встречается в нормальном коде, я не вижу, чтобы правило CA2000 имело значение большинства кодовых баз - существует так много ложных срабатываний вы, скорее всего, пропустите в 1 из 50 случаев, когда это настоящая проблема.

Ответ 3

этот код избавится от обоих предупреждений (Я использую использование (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

Добавьте элемент управления в коллекцию непосредственно после его создания, но прежде чем что-либо сделать с элементом управления.

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

В случае, если вы считаете, что Code Analysis ошибочен (случилось со мной, что он попросил вызвать Dispose для объекта, который не реализовал IDisposable), или вы не чувствуете, что это необходимо для удаления этого объекта, вы всегда можете подавить это сообщение вроде этого.

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