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

Анализатор обнаружил использование виртуального или переопределенного события\. В случае, если данное событие будет переопределено в производном классе, это может привести к непредсказуемому поведению\. 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\.

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

```cpp
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\(\)' будет вывод на консоль одной строки:

```cpp
Handler
```

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

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

```cpp
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'\.