CA2000 передает ссылку на объект на базовый конструктор в С#

Я получаю предупреждение при запуске некоторого кода с помощью утилиты Visual Studio Code Analysis, которую я не уверен, как ее решить. Возможно, кто-то здесь столкнулся с подобной проблемой, разрешил ее и готов поделиться своим пониманием.

Я программирую специально окрашенную ячейку, используемую в элементе управления DataGridView. Код напоминает:

public class DataGridViewMyCustomColumn : DataGridViewColumn
{
    public DataGridViewMyCustomColumn() : base(new DataGridViewMyCustomCell())
    {
    }

Он генерирует следующее предупреждение:

CA2000: Microsoft.Reliability: в методе DataGridViewMyCustomColumn.DataGridViewMyCustomColumn() 'вызов System.IDisposable.Dispose на объекте' new DataGridViewMyCustomCell() ', прежде чем все ссылки на него выходят за рамки.

Я понимаю, что он предупреждает меня, что DataGridViewMyCustomCell (или класс, который он наследует) реализует интерфейс IDisposable, и метод Dispose() следует вызывать для очистки любых ресурсов, заявленных DataGridViewMyCustomCell, когда он больше не работает.

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

Мой вопрос тогда, это код в порядке, как есть? Или, как он может быть реорганизован для разрешения предупреждения? Я не хочу подавлять предупреждение, если это действительно не подходит для этого.

Ответ 1

Если вы используете Visual Studio 2010, то CA2000 полностью сломается. Он также может быть поврежден в других версиях FxCop (анализ кода a.k.a.), но VS2010 является единственным, за который я могу ручаться. Наша кодовая база дает предупреждения CA2000 для кода вроде этого...

internal static class ConnectionManager 
{
    public static SqlConnection CreateConnection()
    {
         return new SqlConnection("our connection string");
    }
}

... указывает, что соединение не удаляется до того, как он выходит из области видимости в методе. Ну, да, это правда, но он не выходит за рамки приложения, поскольку он вернулся к вызывающему, - что вся суть метода! Точно так же ваш аргумент конструктора не выходит за пределы области видимости, а передается базовому классу, поэтому он является ложным положительным от правила, а не фактической проблемой.

Это было полезным правилом, но теперь все, что вы действительно можете сделать, это отключить его, пока они не исправят его. Это печально, потому что (очень немногие) фактические положительные стороны - это то, что должно быть исправлено.

Ответ 2

Существует нет безопасного и элегантного способа, чтобы связанный конструктор передавал новый объект IDisposable базовому конструктору, так как, как вы заметили, невозможно связать цепочный вызов конструктора в любом виде блока try finally. Существует безопасный подход, но он едва ли изящный: определите полезный метод, например:

internal static TV storeAndReturn<TR,TV>(ref TR dest, TV value) where TV:TR
{ 
  dest = value; return value;
}

Попросите конструктора выглядеть примерно так:

protected DataGridViewMyCustomColumn(ref IDisposable cleaner) : 
   base(storeAndReturn(ref cleaner, new DataGridViewMyCustomCell()))
{
}

Коду, которому нужен новый объект, пришлось бы вызвать общедоступный статический метод factory, который вызывал бы соответствующий конструктор в блоке try/finally, чья основная строка отклоняла бы cleaner непосредственно перед его завершением, и чей блок finally будет вызывать Dispose на cleaner, если он не равен null. При условии, что каждый подкласс определяет аналогичный метод factory, этот подход гарантирует, что новый объект IDisposable будет удален, даже если возникает исключение между созданным временем и временем, в течение которого инкапсулирующий объект подвергается клиенту. Образец уродливый, но я не уверен, что какой-либо более хороший шаблон обеспечит правильность.

Ответ 3

Спасибо, Грег. Это мое беспокойство - несколько положительных моментов, которые должны быть исправлены. Ну, возможно, на следующей итерации VS это будет более точно. Я сейчас буду подавлять предупреждение в коде.