Для получения триального ключа
заполните форму ниже
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Бесплатная лицензия PVS-Studio для специалистов Microsoft MVP
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Мне интересно попробовать плагин на:
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
V3161. Comparing value type variables w…
Проверка проектов
Сообщения PVS-Studio
Диагностики общего назначения (General Analysis, C++)
Диагностики общего назначения (General Analysis, C#)
Диагностики общего назначения (General Analysis, Java)
Диагностика микро-оптимизаций (C++)
Диагностика 64-битных ошибок (Viva64, C++)
Реализовано по запросам пользователей (C++)
Cтандарт MISRA
Стандарт AUTOSAR
Стандарт OWASP (C#)
Проблемы при работе анализатора кода
Дополнительная информация
Оглавление

V3161. Comparing value type variables with 'ReferenceEquals' is incorrect because compared values will be boxed.

01 Окт 2020

Анализатор обнаружил подозрительный вызов метода 'Object.ReferenceEquals': один или оба переданных аргумента могут иметь значимый тип. Так как метод принимает параметры типа 'Object', при передаче значимых типов будет произведена упаковка. Таким образом, в 'Object.ReferenceEquals' будет передана ссылка на объект, созданный в куче при упаковке. Так как полученная ссылка не будет равна никакой другой, из этого следует, что метод будет возвращать 'false' независимо от значения переданного аргумента.

Рассмотрим простой синтетический пример:

void SyntheticMethod(Point x, object a)
{
  if (Object.ReferenceEquals(x, a))
    ....
}

В качестве аргументов в 'Object.ReferenceEquals' передаются 'x' и 'a'. Переменная 'x' имеет тип 'Point', следовательно, её значение будет упаковано при преобразовании в 'Object'. Таким образом, вызов 'Object.ReferenceEquals' всегда будет возвращать 'false' вне зависимости от того, какие аргументы переданы в 'SyntheticMethod'.

В качестве исправления можно заменить вызов метода на непосредственную проверку равенства значений:

void SyntheticMethod(Point x, object a)
{
  if (a is Point pointA && pointA == x)
    ....
}

Правило также применяется и к аргументам, типы которых являются generic-параметрами. Если значения такого параметра не ограничены ссылочными типами, то анализатор будет выдавать предупреждение. Пример:

void SomeFunction<T>(T genericObject,
                     object someObject)
{
  if (Object.ReferenceEquals(genericObject, someObject))
    ....
}

Здесь 'genericObject' может быть экземпляром как ссылочного, так и значимого типа. Для значимых типов 'Object.ReferenceEquals' всегда будет возвращать 'false'. Такое поведение может оказаться неожиданным и нежелательным. Чтобы быть уверенным в том, что в метод не будут передаваться объекты значимых типов, нужно добавить соответствующее ограничение параметра:

void SomeFunction<T>(T genericObject, 
                     object someObject) where T : class
{
  if (Object.ReferenceEquals(genericObject, someObject))
    ....  
}

Теперь на месте параметра может быть только ссылочный тип, и анализатор не будет выдавать предупреждение.

Рассмотрим ситуацию, в которой параметр ограничен интерфейсом:

void SomeFunction<T>(T genericObject,
                     object someObject) where T : ICloneable
{
  if (Object.ReferenceEquals(genericObject, someObject))
    ....
}

Анализатор выдаст предупреждение на данный код, так как объекты значимых типов могут реализовывать интерфейсы. Таким образом, ограничение 'where T : ICloneable' не защищает от вызова метода для структур, и результат такого вызова может оказаться неожиданным.

Отдельно рассмотрим одну особенность, связанную с nullable-типами. Они являются значимыми, следовательно, их преобразование в 'Object' приведёт к упаковке. Однако упаковка объектов таких типов производится особым образом. Если переменной присвоено значение, то при упаковке в кучу попадёт не объект nullable-типа, а само значение. Если же значение не задано, то при упаковке будет получена нулевая ссылка.

В следующем примере 'x' имеет тип 'Nullable<Point>'. Вполне возможно, что в результате упаковки 'x' будет возвращена нулевая ссылка. В таком случае вызов 'Object.ReferenceEquals' вернёт 'true', если аргумент 'a' также будет равен 'null'.

void SyntheticMethod(Point? x, object a)
{
  if (Object.ReferenceEquals(x, a))
    ....
}

Тем не менее, на этот код будет выдано предупреждение. Использование такого способа для проверки двух элементов на равенство 'null' выглядит подозрительно. Уместнее будет проверять наличие значений непосредственным сравнением с 'null' или с помощью свойства 'HasValue':

void SyntheticMethod(Point? x, object a)
{
  if (!x.HasValue && a == null)
    ....
}

Дополнительные материалы по теме особенностей nullable-типов:

Данная диагностика классифицируется как:

Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо