Как правильно обрабатывать исключения при выполнении файла IO - PullRequest
15 голосов
/ 17 сентября 2008

Часто я обнаруживаю, что каким-то образом взаимодействую с файлами, но после написания кода я всегда сомневаюсь, насколько он на самом деле. Проблема в том, что я не совсем уверен, как файловые операции могут завершиться неудачей и, следовательно, лучший способ обработки ожиданий.

Казалось бы, простое решение - просто перехватить любые исключения IOException, генерируемые кодом, и дать пользователю сообщение об ошибке «Недоступный файл», но возможно ли получить более подробные сообщения об ошибках. Есть ли способ определить разницу между такими ошибками, как файл, заблокированный другой программой, и данными, которые невозможно прочитать из-за аппаратной ошибки?

Учитывая следующий код C #, как бы вы обрабатывали ошибки удобным (максимально информативным) способом?

public class IO
{
   public List<string> ReadFile(string path)
   {
      FileInfo file = new FileInfo(path);

      if (!file.Exists)
      {
         throw new FileNotFoundException();
      }

      StreamReader reader = file.OpenText();
      List<string> text = new List<string>();

      while (!reader.EndOfStream)
      {
         text.Add(reader.ReadLine());
      }

      reader.Close();
      reader.Dispose();
      return text;
   }

   public void WriteFile(List<string> text, string path)
   {
      FileInfo file = new FileInfo(path);

      if (!file.Exists)
      {
         throw new FileNotFoundException();
      }

      StreamWriter writer = file.CreateText();

      foreach(string line in text)
      {
         writer.WriteLine(line);
      }

      writer.Flush();
      writer.Close();
      writer.Dispose();
   }
}

Ответы [ 7 ]

13 голосов
/ 18 сентября 2008

... но возможно ли получить более подробные сообщения об ошибках.

Да. Идите вперед и поймайте IOException и используйте метод Exception.ToString(), чтобы отобразить относительно релевантное сообщение об ошибке. Обратите внимание, что исключения, сгенерированные .NET Framework, будут содержать эти полезные строки, но если вы собираетесь выдать свое собственное исключение, вы должны не забыть вставить эту строку в конструктор Exception, например:

throw new FileNotFoundException("File not found");

Кроме того, согласно Скотту Дорману , используйте это утверждение using. Однако следует отметить, что утверждение using на самом деле ничего не catch, как это и должно быть. Например, ваш тест, чтобы увидеть, существует ли файл, представит условие гонки, которое может быть скорее досадным . Это на самом деле не приносит никакой пользы. Итак, теперь для читателя имеем:

try {  
    using (StreamReader reader = file.OpenText()) {  
        // Your processing code here  
    }  
} catch (IOException e) {  
    UI.AlertUserSomehow(e.ToString());  
}

Короче говоря, для основных файловых операций:
1. Используйте using
2. Оберните оператор использования или функцию в try / catch, catch es IOException
3. Используйте Exception.ToString() в вашем catch, чтобы получить полезное сообщение об ошибке
4. Не пытайтесь самостоятельно обнаруживать исключительные проблемы с файлами. Пусть .NET сделает бросок за вас.

7 голосов
/ 17 сентября 2008

Первое, что вы должны изменить, это ваши вызовы StreamWriter и StreamReader, чтобы обернуть их в оператор использования, например:

using (StreamReader reader = file.OpenText())
{
   List<string> text = new List<string>();
   while (!reader.EndOfStream)
   {
      text.Add(reader.ReadLine());
   }
}

Это позаботится о вызове Close и Dispose для вас и фактически обернет его в блок try / finally, чтобы фактический скомпилированный код выглядел следующим образом:

StreamReader reader = file.OpenText();
try
{
   List<string> text = new List<string>();
   while (!reader.EndOfStream)
   {
      text.Add(reader.ReadLine());
   }
}
finally
{
   if (reader != null)
      ((IDisposable)reader).Dispose();
}

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

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

1 голос
/ 17 сентября 2008

Не вижу смысла проверять наличие файла и выдавать исключение FileNotFoundException без сообщения. Фреймворк сама выдаст исключение FileNotFoundException с сообщением.

Другая проблема с вашим примером заключается в том, что вы должны использовать шаблон try / finally или оператор using, чтобы гарантировать, что ваши одноразовые классы будут правильно расположены, даже если есть исключение.

Я бы сделал это примерно так, перехватил бы любое исключение за пределами метода и отобразил бы сообщение об исключении:

public IList<string> ReadFile(string path)
{
    List<string> text = new List<string>();
    using(StreamReader reader = new StreamReader(path))
    {
      while (!reader.EndOfStream)      
      {         
         text.Add(reader.ReadLine());      
      }
    }
    return text;
}
1 голос
/ 17 сентября 2008
  • Пропустить File.Exists (); или обработайте это в другом месте или позвольте CreateText () / OpenText () поднять это.
  • Конечный пользователь, как правило, заботится только об этом или нет. Если не получится, просто скажите, он не хочет подробностей.

Я не нашел встроенного способа узнать подробности о том, что и почему что-то не получилось в .NET, но если вы переходите на нативную версию CreateFile, у вас есть тысячи кодов ошибок, которые могут сказать вам, что пошло не так.

0 голосов
/ 17 сентября 2008

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

Сумма для этого может быть следующая статья http://goit -postal.blogspot.com / 2007/03 / краткое введение-в-исключение.

0 голосов
/ 17 сентября 2008

Я бы использовал оператор using для упрощения закрытия файла. См. MSDN C #, используя оператор

Из MSDN:

  using (TextWriter w = File.CreateText("log.txt")) {
     w.WriteLine("This is line one");
     w.WriteLine("This is line two");
  }
  using (TextReader r = File.OpenText("log.txt")) {
     string s;
     while ((s = r.ReadLine()) != null) {
        Console.WriteLine(s);
     }
  }
0 голосов
/ 17 сентября 2008

Я бы попробовал проверить файл. Существует до вызова вашего чтения / записи и ответа на него там, вместо того, чтобы создавать накладные расходы, связанные с выдачей ошибки и ее перехватом позже, поскольку эту проверку очень легко выполнить. Я понимаю необходимость возникновения ошибок, но в этом конкретном случае простая проверка imho будет лучшим решением. Я имею в виду добавление еще одного метода, чтобы проверить, существует ли файл.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...