Не доверять File.Exists() и использовать try/catch?

219
09 января 2018, 11:38

На одной из страниц форума https://stackoverflow.com/a/6186400/2948684, где идет обсуждение if/else vs. try/catch автор ответа все же рекомендует код, который изменяет состояние приложения с помощью внешних источников, обвертывать в блок try/catch и приводя код

if(File.Exists("file.txt"))
    File.Delete("file.txt")

говорит, что даже если выражение File.Exists("file.txt") вернет true, то перед тем как стек успеет перейти к File.Delete("file.txt"), что-то может изменить файл извне.

Я удивился, неужели метод File.Exists() выполняется так медленно. Да я также читал на других страницах, что в случае с File.Exists() try/catch быстрее .

И тем не менее, в эти миллисекунды разве может что-то произойти. Да и эти миллисекунды решил подсчитать. К примеру, что на SSD, что на HDD проверка существования файла в корне диска заняла у метода File.Exists() в среднем 12 миллисекунд. Конечно, если сервер будет обращаться по локалке к отдельному файловому серверу это займет чуть больше, но ведь чуть.

Вопрос

Ну а вопрос мой будет звучать так: если я правильно понял автора, то какой сценарий на практике предполагает возможность такой ситуации? Быть может что-то такое может произойти:

  1. Процесс A начал переименовывать файла и завис;
  2. Процесс Б выполнил File.Exists() и вернул true;
  3. Процесс A ожил и переименовал файл;
  4. Процесс Б выполняет File.Delete() и вернул исключение.
Answer 1

Да, именно так.

Об этом пишет Эрик Липперт в одной из лучших своих статей Vexing Exceptions:

Я приведу фрагмент в собственном переводе, а вы обязательно прочтите оригинал.

И наконец, исключения внешнего мира немного похожи на досадные исключения с той разницей, что они не являются результатом ошибок в дизайне. Скорее, они отражают вторжение грубой, неизящной реальности в замечательную, кристально чистую логику вашей программы. Посмотрите на этот C#-псевдокод:

try
{
  using ( File f = OpenFile(filename, ForReading) )
  {
    // что-то делаем
  }
}
catch (FileNotFoundException)
{
  // обработать ненайденный файл
}

Можно ли избавиться от блока try/catch?

if (!FileExists(filename))
  // обработать ненайденный файл
else
  using ( File f = ...

Это уже не та же самая программа. Теперь в ней есть «race condition». Какой-то другой процесс мог удалить, залочить, переместить файл, или поменять права доступа между FileExists и OpenFile.

Может быть, мы просто недостаточно хитры? Может, можно как-то заблокировать файл? Нет, это не поможет. Носитель может быть удалён пользователем из дисковода, сеть может упасть…

Вы обязаны ловить исключения внешнего мира, потому что они всегда могут произойти, как бы вы не старались их избежать: это события внешнего мира, у вас нет над ними контроля.

Конкретно отвечая на ваш вопрос: проверка существования может быть и быстрой, но удаление файла могло начаться и до начала этой проверки. И закончиться как раз к её концу.

READ ALSO
Проверка TextBox'ов на пустоту

Проверка TextBox'ов на пустоту

Использую для проверки на пустоту такой способ

240
В чем суть проверки if (bouncers[index] ==null)…?

В чем суть проверки if (bouncers[index] ==null)…?

Само выражение выглядит как if (bouncers[index] == null)Вопрос, для чего происходит такая проверка?

212
Повторный вопрос с уточнением

Повторный вопрос с уточнением

Недавно задавал такой вопрос на счет списка строк: Поиск строк в массиве по уменьшению их кол-ства

289
как посмотреть сборку в C#?

как посмотреть сборку в C#?

есть программа, которая компилируется в исполняемый файл exe, есть ли методы в C# которые позволяют заглянуть в этот exe? меня интересуют какие...

385