# V3178. Calling method or accessing property of potentially disposed object may result in exception.

Анализатор обнаружил вызов метода или обращение к свойству объекта, у которого ранее был вызван метод 'Dispose' или его аналог. Подобные действия могут привести к выбрасыванию исключения.

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

    public void AppendFileInformation(string path)
    {
      FileStream stream = new FileStream(path,
                                         FileMode.Open,
                                         FileAccess.Read);
      ....
      stream.Close();
      ....
      if (stream.Length == stream.Position)
      {
        Console.WriteLine("End of file has been reached.");
        ....
      }
      ....
    }

В условии проверяется, что файл был прочитан полностью. Для проверки сравнивают текущую позицию потока и его длину. Проблема в том, что при обращении к свойству 'Length' будет выброшено исключение типа 'ObjectDisposedException'. Это связано с тем, что перед условием у переменной 'stream' вызывается метод 'Close'. Для класса 'FileStream' этот метод является аналогом 'Dispose'. Следовательно, ресурсы 'stream' будут освобождены.

Рассмотрим корректную реализацию 'AppendFileInformation':

    public void AppendFileInformation(string path)
    {
      using (FileStream stream = new FileStream(path,
                                                FileMode.Open,
                                                FileAccess.Read))
      {
        ....
        if (stream.Length == stream.Position)
        {
          Console.WriteLine("End of file has been reached.");
        }
        ....
      }
      ....
    }

Для корректной работы метода стоит использовать выражение 'using'. В этом случае:

- ресурсы объекта 'stream' будут автоматически освобождены после выхода из тела 'using';
- вне тела 'using' обратиться к объекту не получится – это даёт дополнительную защиту от исключений типа 'ObjectDisposedException';
- даже если в теле 'using' возникнет исключение, ресурсы всё равно будут освобождены.

Ещё один вариант ошибки выглядит следующим образом:

    public void ProcessFileStream(FileStream stream)
    {
      ....
      bool flag = CheckAndCloseStream(stream);
      AppendFileInformation(stream);
      ....
    }

    public bool CheckAndCloseStream(FileStream potentiallyInvalidStream)
    {
      ....
      potentiallyInvalidStream.Close();
      ....
    }

    public void AppendFileInformation(FileStream streamForInformation)
    {
      ....
      if (streamForInformation.Length == streamForInformation.Position)
      {
        Console.WriteLine("End of file has been reached.");
      }
      ....
    }

После вызова 'CheckAndCloseStream' в методе 'ProcessFileStream' ресурсы объекта, на который ссылается переменная 'stream', будут освобождены. Далее эта переменная передаётся в метод 'AppendFileInformation'. Внутри него производится обращение к свойству 'Length', из-за чего будет выброшено исключение типа 'ObjectDisposedException'.

Корректная реализация 'ProcessFileStream' будет выглядеть следующим образом:

    public void ProcessFileStream(FileStream stream)
    {
      ....
      AppendFileInformation(stream);
      bool flag = CheckAndCloseStream(stream);
      ....
    }

Был изменён порядок вызовов методов 'CheckAndCloseStream' и 'AppendFileInformation'. Вследствие этого ресурсы будут освобождаться уже после выполнения действий над потоком. Следовательно, исключение выброшено не будет.

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

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

CWE-672