Чи повинен мій метод створювати власний виняток, або дозволяти .NET кидати, якщо файл не існує?


75

Ось мій код:

public void ReadSomeFile(string filePath)
{
    if (!File.Exists(filePath))
        throw new FileNotFoundException();

    var stream = new FileStream(filePath, ....)
    .....
}

Чи повинен я сам робити виключення (див. File.ExistsПеревірку)? FileStreamвже викине, FileNotFoundExceptionякщо файл не існує. Яка тут хороша практика програмування? Аналіз коду говорить, що нам слід перевірити наші параметри. Але якщо я передаю цей параметр безпосередньо іншому методу (мій чи інший код), і цей метод викине сам виняток, то яка перевага перевірки аргументу в моєму коді?


25
Немає сенсу кидати FileNotFoundException- насправді, це просто запрошення до питань стану перегонів. Або ви обробляєте виняток, дозволяєте йому поширюватися, або обертаєте його у власному винятку. Це відповідає "я знаю, що з цим робити", "я не знаю, що з цим робити" і "я хочу обробити це вище в стеці" відповідно.
Луаан,

Примітка: якщо вам дійсно потрібно перевірити аргументи, ви можете перевірити, чи filePathвиглядає справжнім (тобто абсолютний шлях, або, принаймні, не містить Path.GetInvalidFileNameChars())
Олексій Левенков

2
@AlexeiLevenkov Я думаю, що це теж не потрібно. FileStream це впорається,
fhnaseer

3
Пов’язане: З блогу Еріка Ліпперта - Неприємні винятки - Цей випадок підпадає під „екзогенні винятки” .
Кобі

це насправді залежить від того, чого ви хочете досягти. тож на це немає "правильної відповіді". це залежить
вт

Відповіді:


118

if (File.Exists(f)) { DoSomething(f) }(або його заперечення) є анти-зразком. Файл можна видалити або створити між цими двома твердженнями, тому мало сенсу перевіряти його існування таким чином.

Окрім цього, як зазначено в коментарях, хоча це File.Exists()може повернутися істиною, фактичне відкриття файлу може все-таки не вдатися з різних причин. Отже, вам доведеться повторити перевірку помилок і розкидати відкриття файлу.

Оскільки ви не хочете повторювати себе, а замість цього зберігайте свій код СУХИМ, просто спробуйте відкрити файл і нехай new FileStream()кине. Тоді ви можете зловити виняток і, якщо хочете, перекинути оригінал або викид для конкретної програми.

Звичайно, дзвінок File.Exists()можна виправдати, але не за такою схемою.


3
@CodeCaster Звичайно. І якийсь інший процес може створити файл між File.Open(f);киданням і тим, що ви його зловите. Проблема полягає в тому, що ситуація може змінитися до того, як ви впораєтеся з цим (оскільки цього майже не уникнути у випадку відмови), а в тому, що вам слід перевірити помилку один раз, щоб уникнути повторення (і ви все одно повинні перевірити помилку при відкритті). (Також зауважте, що під час спілкування з деякими файловими системами успішне відкриття не означає, що файл не буде видалено з вас: тому навіть там помилка може статися після того, як відкриття спрацює.)
Якк - Адам Невраумонт

2
Окрім перегонового стану, цей код ігнорує ситуацію, коли файл існує, але його не можна прочитати (через дозволи тощо), а можливо навіть більше помилок.
el.pescado

8
Хоча перевірка наявності файлу та негайна спроба його відкрити, як правило, є анти-шаблоном, я не вважав би його анти-шаблоном, якби інша логіка розділяла перевірку та відкриття. У деяких мережевих системах перевірка наявної доступності файлу може бути дешевшою, ніж отримання блокування, необхідного для його відкриття. Якщо код не зможе зробити нічого корисного, якщо не буде доступно декілька файлів, може бути корисно переконатися, що всі файли здаються доступними, перш ніж отримати блокування, необхідні для відкриття будь-якого з них.
supercat

3
Python має політику під назвою "Простіше просити прощення, ніж про дозвіл" (EAFP). Це означає, що іноді, через умови перегонів та речі, які не під вашим контролем, найкраще просто попросити програму зробити те, що вона хоче, і усунути всі можливі помилки там (винятки), а не перевіряти на кожному кроці, чи умови оптимальні. У цьому прикладі я думаю, що EAFP може бути застосований до найкращих практик.
sleblanc

1
Також може статися FileNotFoundException, оскільки користувач не має дозволу на його перегляд або папка не існує ... тому, якщо ви хочете допомогти користувачеві, ви могли б, у ловлі, перевірити для більш конкретних причин, наприклад Folder.Exists - або просто переконайтеся, що у вашому доброзичливому повідомленні користувача написано "файл неможливо знайти або отримати до нього доступ"!
AndrewD

15

Ваш метод викликається ReadSomeFileі бере filenameяк вхідне значення a , тому розумно для нього кинути a FileNotFoundException. Оскільки ви не можете додати жодного значення, зловивши виняток, а потім кинувши його самостійно, просто дозвольте .NET кинути його.

Однак якщо ваш метод був, LoadData(databaseName)і йому потрібно отримати доступ до багатьох файлів, ловити виняток і створювати спеціальний виняток може бути корисним, оскільки ви можете додати databaseNameвиняток разом з іншою корисною інформацією.


І навіть в останньому випадку вам слід спробувати зловити внутрішній виняток, а не використовувати File.Exists.
Стіг Хеммер

це той випадок, коли відповідь, яка насправді відповідає на запитання, отримує набагато менше голосів
Андрій Тиличко

1

Окрім уже наданих відповідей, ви також можете сказати, що це залежить від того, що ви очікуєте.

Якщо ви хочете прочитати файл журналу, а він не існує, ви хочете видавати помилку або просто порожній рядок (або порожній масив рядків)?

Якщо повертається значення за замовчуванням (як порожній рядок), я просто обертаю вміст функції в try-catch(але лише для очікуваних помилок) і повертаю значення за замовчуванням у catchблок, повертаючи фактичний вміст у tryблок.

Це залишило б три можливі ситуації:

  1. Повертається вміст файлу;
  2. Повертається значення за замовчуванням, оскільки сталася очікувана помилка;
  3. .NET видає помилку, оскільки ви не виявили цієї конкретної помилки.

0

Нехай правильний метод спробує відкрити файл, поки ви не уявляєте повне ім’я файлу, щось на зразок спеціальних назв файлів (наприклад, файли пристроїв та шляхи UNC ):

У деяких випадках інші методи файлів можуть бути невдалими, але відкриття файлу вдається.

Ось приклади спеціальних імен файлів:

  • ПРОТИВ
  • НУЛ
  • COM1, COM2, COM3, COM4
  • \\ server \ share \ file_path
  • \\ teela \ admin $ \ system32 (щоб досягти C: \ WINNT \ system32)
  • C: .. \ File.txt
  • \\. \ COM1
  • % TEMP%
  • і більше...

2
Я не дуже багато знаю про Windows, але я впевнений, що% TEMP% - це змінна середовища, а не файл, і що функція "відкрити файл" не розширить її.
Functino
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.