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

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

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

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

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

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

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


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

>
>
>
V3119. Calling a virtual (overridden) e…
Сообщения PVS-Studio
Диагностики общего назначения (General Analysis, C++)
Диагностики общего назначения (General Analysis, C#)
Диагностики общего назначения (General Analysis, Java)
Диагностика микро-оптимизаций (C++)
Диагностика 64-битных ошибок (Viva64, C++)
Cтандарт MISRA
Стандарт AUTOSAR
Стандарт OWASP (C#)
Дополнительная информация
Оглавление

V3119. Calling a virtual (overridden) event may lead to unpredictable behavior. Consider implementing event accessors explicitly or use 'sealed' keyword.

03 Ноя 2016

Анализатор обнаружил использование виртуального или переопределенного события. В случае, если данное событие будет переопределено в производном классе, это может привести к непредсказуемому поведению. MSDN не рекомендует использование переопределенных виртуальных событий: "Do not declare virtual events in a base class and override them in a derived class. The C# compiler does not handle these correctly and it is unpredictable whether a subscriber to the derived event will actually be subscribing to the base class event". https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-raise-base-class-events-in-derived-classes.

Рассмотрим пример:

class Base
{
  public virtual event Action MyEvent;
  public void FooBase() { MyEvent?.Invoke(); }
}
class Child: Base
{
  public override event Action MyEvent;
  public void FooChild() { MyEvent?.Invoke(); }
}
static void Main()
{
  var child = new Child();
  child.MyEvent += () => Console.WriteLine("Handler");
  child.FooChild();
  child.FooBase();
}

Несмотря на то, что производится вызов обоих методов 'FooChild()' и 'FooBase()', результатом работы метода 'Main()' будет вывод на консоль одной строки:

Handler

Используя отладчик или тестовый вывод можно убедиться в том, что при вызове 'child.FooBase()' значение переменной 'MyEvent' будет равно 'null'. Таким образом, подписывание на событие 'MyEvent' для класса 'Child', наследника 'Base', переопределяющего это событие, не привело к подписыванию на событие 'MyEvent' базового класса. На первый взгляд, такое поведение не согласуется с поведением, например, виртуальных методов, однако его можно объяснить особенностью реализации событий в C#. При объявлении события компилятор автоматически создаёт для него 2 метода-аксессора 'add' и 'remove', и поле-делегат, в которое происходит добавление\удаление делегатов при подписке\отписке на события. В случае виртуального события, базовый и дочерний классы будут иметь индивидуальные (не виртуальные) поля, связанные с данным событием.

Данной проблемы можно избежать, объявив для события его аксессоры в явном виде:

class Base
{
  public virtual Action _myEvent { get; set; }
  public virtual event Action MyEvent
  {
    add
    {
      _myEvent += value;
    }
    remove
    {
      _myEvent -= value;
    }
  }
  public void FooBase() { _myEvent?.Invoke(); }
}

Мы настоятельно не рекомендуем использовать виртуальные или переопределенные события описанным в первом примере образом. В случае, если вы все же вынуждены использовать переопределенные события (например, при наследовании от абстрактного класса), используйте их с осторожностью, учитывая возможность неопределенного поведения. Используйте явное определение аксессоров 'add' и 'remove' либо используйте при объявлении класса или события ключевое слово 'sealed'.

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

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