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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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