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