Как правильно обрабатывать исключения при выполнении файла io

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

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

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

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();
   }
}

Ответы

Ответ 1

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

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

throw new FileNotFoundException("File not found");

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

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

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

Ответ 2

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

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, которого может быть достаточно для ваших пользователей, но это может быть не так.

Ответ 3

  • Пропустить файл .Exists(); либо обрабатывать его в другом месте, либо позволить CreateText()/OpenText() поднять его.
  • Конечный пользователь обычно заботится только об успешном завершении или нет. Если это не удастся, просто скажите, он не хочет деталей.

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

Ответ 4

Я не вижу смысла проверять наличие файла и бросать исключение 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;
}

Ответ 5

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

Из 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);
     }
  }

Ответ 6

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

Подводя итог, может быть следующая статья http://goit-postal.blogspot.com/2007/03/brief-introduction-to-exception.html.

Ответ 7

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

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