CA2213 правило анализа кода и автоматически реализованные свойства

Я использую статический анализ кода в наших проектах для проверки нарушений кода. Одним из широко используемых правил является CA2213, который проверяет правильность размещения одноразовых полей.

Я заметил, что CA2213 не проверяет возможность утилизации автоматически реализованных свойств.

Кроме того, CA2213 не проверяет размещение ни полей, ни автоматически реализованных свойств, если класс наследует класс, который реализует IDisposable и не переопределяет метод Dispose.

Практический пример:

public sealed class Good : IDisposable {
    private Font font;
    public Font Font {
        get { return font; }
        set { font = value; }
    }
    public Good() { font = new Font("Arial", 9); }
    public void Dispose() { /* Do nothing */ }       // CA2213
}

public sealed class Bad : IDisposable {
    public Font Font { get; set; }
    public Bad() { Font = new Font("Arial", 9); }
    public void Dispose() { /* Do nothing */ }       // No warning
}

Кто-нибудь еще сталкивался с таким поведением? Это по дизайну или ошибке в правиле CA2213?

Ответы

Ответ 1

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

CA2213 Одноразовые поля должны быть расположены
"Плохо" содержит поле <Font>k__BackingField, которое имеет тип IDisposable: "Шрифт". Измените метод Dispose на "Bad", чтобы вызвать Dispose или Close в этом поле.

Yikes, конечно, в программе нет такого поля. Чтобы создать лучшее сообщение, анализатор должен был бы выяснить, что поле <Font>k__BackingField в метаданных фактически связано с автоматически реализованным свойством Font. В метаданных нет ничего доступного, что делает это соединение однозначно. Поле содержит только атрибут [CompilerGenerated], а автоматически генерируемое имя поля является деталью реализации компилятора. Различные компиляторы языка генерируют разные имена.

Это та проблема, которая требует анализа исходного кода, а не анализа IL, как это было в настоящее время реализовано. Возможность стучать в дверь, анализ исходного кода будет намного проще для реализации с Roslyn, доступным сегодня. VS2015 - первая версия VS, которая поддерживает анализ "Live Code". Я еще не знаю, скоро ли он ищет ошибки стиля CA2213.

Ответ 2

Инструменты статического анализа настроены на хороший компромисс между генерированием слишком большого количества ложных срабатываний (т.е. испусканием проблем с совершенно точным кодом) и слишком большим количеством ложных негативов (т.е. отсутствие реальных проблем в коде).

Возьмем простой пример:

// CA2213 detects an issue
class OwnsTheField : IDisposable
{
    private MemoryStream f = new MemoryStream();
    public void Dispose() {}
}

В этом случае ясно, что следует сообщить о проблеме, и это действительно так.

Теперь сделаем вещи немного сложнее:

// CA2213 does not detect
class FieldIsInjected : IDisposable
{
    private MemoryStream f;
    public FieldIsInjected(MemoryStream p)
    {
        f = p;
    }
    public void Dispose() { }
}

В этом случае может не возникнуть проблемы, если не утилизировать f. Если p правильно настроен вызывающим, тогда все работает как ожидалось:

using (var p = new MemoryStream())
{
    using (var x = FieldIsInjected(p)) { /* ... */ }
} // p is properly disposed

Чаще всего точное поведение (включая обработку всех угловых случаев) не будет задокументировано, поскольку исчерпывающая документация будет слишком дорогостоящей для поддержания с течением времени. Например, это исключение явно упоминается в https://msdn.microsoft.com/en-us/library/ms182328.aspx

Ответ 3

Правило от .NET framework 2 и auto свойства - от С# 3, который был добавлен в спецификацию языка после создания правила. Правило определяется в терминах полей не свойств, поэтому кажется, что оно связано с обновлением, но работает как указано в настоящее время.

Этот вопрос об утилизации свойств, похоже, показывает, что свойства, реализующие IDisposable, не являются согласованными в рамках.

Ответ 4

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