# V3149. Dereferencing the result of 'as' operator can lead to NullReferenceException.

Анализатор обнаружил небезопасное разыменование результата приведения типа через оператор 'as'.

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

    void Foo()
    {
      BaseItem a = GetItem();
      var b = a as SpecificItem;
      b.Bar();
    }

Возвращаемое методом значение может иметь тип, отличный от типа, к которому мы его приводим. Тогда при приведении типа с помощью оператора 'as' в переменную 'b' будет записано значение null. Хотя при самом приведении типа ошибки не будет, при последующем использовании этой переменной без проверки произойдёт выброс исключения 'NullReferenceException'. Исправленный код будет выглядеть так:

    void Foo()
    {
      BaseItem a = GetItem();
      var b = a as SpecificItem;
      b?.Bar();
    }

Если вы уверены, что runtime тип значения у переменной, приводимой с помощью оператора 'as', всегда может быть успешно приведён к указанному типу, то лучше воспользоваться оператором явного приведения типа:

    void Foo()
    {
      BaseItem a = GetItem();
      var b = (SpecificItem)a;
      b.Bar();
    }

В этом случае, если в будущем поведение программы поменяется и метод 'GetItem' перестанет всегда возвращать значение, гарантированно приводимое к заданному типу, в месте приведения будет сгенерировано исключение 'InvalidCastException', что позволит сразу идентифицировать проблемное место в коде. Напротив, используя оператор 'as' в такой ситуации, можно получить 'NullReferenceException' далее по коду, когда приведённая неуспешно переменная, содержащая null, будет разыменована. Причём это может произойти далеко от места, где производится приведение типов, например в другом методе, что затруднит локализацию и исправление такой ошибки.

Также диагностика указывает на возможные опечатки при проверках типа:

    void Foo()
    {
      IDisposable a = GetItem();
      if(a is NonSpecificItem)
      {
        var b = a as SpecificItem;
        b.Bar();
      }
    }

В данном примере типы SpecificItem и NonSpecificItem не связаны друг с другом и результатом приведения будет нулевой указатель. Для защиты от таких опечаток можно использовать возможности C# 7.0 и сделать проверку через Is Type Pattern синтаксис:

    void Foo()
    {
      IDisposable a = GetItem();
      if(a is NonSpecificItem item)
      {
        item.Bar();
      }
    }

Приведём пример работы диагностики на коде из открытого проекта:

    ....
    FuelDefinition = MyDefinitionManager.Static.GetPhysicalItemDefinition(FuelId);
    MyDebug.AssertDebug(FuelDefinition != null);
    ....
    String constraintTooltip = FuelDefinition.DisplayNameText;

В данном примере метод 'GetPhysicalItemDefinition' возвращает объект типа MyPhysicalItemDefinition, полученный из массива объектов базового типа 'MyDefinitionBase':

    public MyPhysicalItemDefinition GetPhysicalItemDefinition(MyDefinitionId id)
    {
      ....
      return m_definitions.m_definitionsById[id] as MyPhysicalItemDefinition;
    }

При этом после вызова метода 'GetPhysicalItemDefinition' присутствует проверка результата приведения на null (MyDebug.AssertDebug), что указывает на потенциальную возможность получения из метода объекта неподходящего типа. Однако данная проверка будет работать только в Debug версии приложения. В Release версии неудачное приведение приведёт к разыменованию нулевой ссылки далее по ходу выполнения программы (FuelDefinition.DisplayNameText).

Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки разыменования нулевого указателя/нулевой ссылки [* см. примечание касательно языков C#, Java].

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

CWE-476