C# 执行文件io时如何正确处理异常

C# 执行文件io时如何正确处理异常,c#,.net,file-io,exception-handling,C#,.net,File Io,Exception Handling,我经常发现自己以某种方式与文件进行交互,但在编写代码之后,我总是不确定它的实际用途。问题是,我不完全确定与文件相关的操作如何失败,因此也不确定处理期望的最佳方法 简单的解决方案似乎只是捕获代码引发的任何IOException,并向用户提供“不可访问的文件”错误消息,但是否有可能获得更细粒度的错误消息。有没有办法确定文件被另一个程序锁定等错误与由于硬件错误而无法读取的数据之间的差异 给定以下C#代码,您将如何以用户友好(尽可能提供信息)的方式处理错误 公共类IO { 公共列表读取文件(字符串路径)

我经常发现自己以某种方式与文件进行交互,但在编写代码之后,我总是不确定它的实际用途。问题是,我不完全确定与文件相关的操作如何失败,因此也不确定处理期望的最佳方法

简单的解决方案似乎只是捕获代码引发的任何IOException,并向用户提供“不可访问的文件”错误消息,但是否有可能获得更细粒度的错误消息。有没有办法确定文件被另一个程序锁定等错误与由于硬件错误而无法读取的数据之间的差异

给定以下C#代码,您将如何以用户友好(尽可能提供信息)的方式处理错误

公共类IO
{
公共列表读取文件(字符串路径)
{
FileInfo file=新的FileInfo(路径);
如果(!file.Exists)
{
抛出新的FileNotFoundException();
}
StreamReader=file.OpenText();
列表文本=新列表();
而(!reader.EndOfStream)
{
text.Add(reader.ReadLine());
}
reader.Close();
reader.Dispose();
返回文本;
}
public void WriteFile(列表文本、字符串路径)
{
FileInfo file=新的FileInfo(路径);
如果(!file.Exists)
{
抛出新的FileNotFoundException();
}
StreamWriter writer=file.CreateText();
foreach(文本中的字符串行)
{
writer.WriteLine(行);
}
writer.Flush();
writer.Close();
writer.Dispose();
}
}

您应该更改的第一件事是对StreamWriter和StreamReader的调用,以将它们包装在using语句中,如下所示:

using (StreamReader reader = file.OpenText())
{
   List<string> text = new List<string>();
   while (!reader.EndOfStream)
   {
      text.Add(reader.ReadLine());
   }
}
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();
}
使用(StreamReader=file.OpenText())
{
列表文本=新列表();
而(!reader.EndOfStream)
{
text.Add(reader.ReadLine());
}
}
这将负责为您调用Close和Dispose,并实际将其包装在try/finally块中,因此实际编译的代码如下所示:

using (StreamReader reader = file.OpenText())
{
   List<string> text = new List<string>();
   while (!reader.EndOfStream)
   {
      text.Add(reader.ReadLine());
   }
}
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();
}
StreamReader=file.OpenText();
尝试
{
列表文本=新列表();
而(!reader.EndOfStream)
{
text.Add(reader.ReadLine());
}
}
最后
{
if(读卡器!=null)
((IDisposable)读取器).Dispose();
}
这里的好处是,即使发生异常,您也可以确保流被关闭

至于任何更显式的异常处理,它实际上取决于您希望发生什么。在您的示例中,您显式测试该文件是否存在,并抛出FileNotFoundException,这可能对您的用户来说足够了,但可能不够。

  • 跳过文件.Exists();要么在别处处理它,要么让CreateText()/OpenText()引发它
  • 最终用户通常只关心它是否成功。如果失败了,就这么说,他不想知道细节

我还没有找到一种内置的方法来获取有关.NET中出现故障的原因和原因的详细信息,但是如果您使用CreateFile进行本机操作,您会有数千个错误代码,可以告诉您出了什么问题

我将使用using语句简化关闭文件的过程。看

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

我不认为检查文件是否存在并抛出一个没有消息的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;
}
public IList读取文件(字符串路径)
{
列表文本=新列表();
使用(StreamReader=新StreamReader(路径))
{
而(!reader.EndOfStream)
{         
text.Add(reader.ReadLine());
}
}
返回文本;
}

也许这不是您想要的,但请重新考虑您正在使用的异常处理类型。首先,异常处理不应被视为“用户友好”,至少只要您将程序员视为用户即可

这方面的总结可能是以下文章

…但是否有可能获得更细粒度的错误消息

对。继续并捕获
IOException
,然后使用
Exception.ToString()
方法获取要显示的相对相关的错误消息。请注意,.NET Framework生成的异常将提供这些有用的字符串,但如果您要抛出自己的异常,则必须记住将该字符串插入到
异常的构造函数中,如:

抛出新的FileNotFoundException(“未找到文件”)

另外,绝对地,按照,使用
using
语句。但是需要注意的是,
using
语句实际上并没有捕获任何内容,这是它应该的方式。例如,您测试该文件是否存在,将引入一种竞争条件,这种竞争条件可能非常复杂。把它放在里面对你没有任何好处。因此,现在,对于读者,我们有:

try {  
    using (StreamReader reader = file.OpenText()) {  
        // Your processing code here  
    }  
} catch (IOException e) {  
    UI.AlertUserSomehow(e.ToString());  
}
简而言之,对于基本文件操作:
1.使用
使用

2、将using语句或函数包装在
try
/
catch
中,该
catch
es
IOException

3.在
catch
中使用
Exception.ToString()
,以获取有用的错误消息

4.不要试图自己检测异常的文件问题。让.NET来帮你做吧。

我实际上知道using指令,但忘了它。然而,我不知道它也增加了一个尝试捕捉。酷!实际上,它添加了try/finally而不是try/catch。这就是它如何确保即使在发生异常时也调用Dispose的方法