[通常,我发现自己以某种方式与文件进行交互,但是在编写代码之后,我总是不确定它实际上是多么鲁棒。问题是我不确定文件相关操作如何会失败,因此无法确定处理期望的最佳方法。
简单的解决方案似乎只是捕获代码引发的所有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();
}
}
...但是有可能获得更多细粒度的错误消息。
是。继续并捕获IOException
,并使用Exception.ToString()
方法获取要显示的相对相关的错误消息。请注意,.NET Framework生成的异常将提供这些有用的字符串,但是如果您要抛出自己的异常,则必须记住将该字符串插入Exception
的构造函数中,例如:
throw new FileNotFoundException("File not found");
而且,绝对地,根据Scott Dorman,请使用该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
/ IOException
中3.在Exception.ToString()
中使用catch
获得有用的错误消息4.不要试图自己检测出异常的文件问题。让.NET为您代劳。
您应该更改的第一件事是对StreamWriter和StreamReader的调用,以将它们包装在using语句中,如下所示:
using (StreamReader reader = file.OpenText())
{
List<string> text = new List<string>();
while (!reader.EndOfStream)
{
text.Add(reader.ReadLine());
}
}
这将为您调用Close and 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,这对于您的用户而言可能足够,但可能不足够。
我还没有找到一种内建的方法来获取有关.NET失败的原因和原因的详细信息,但是,如果使用CreateFile进行本机处理,则会有成千上万的错误代码可以告诉您出了什么问题。
我看不出检查文件是否存在并抛出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;
}
我将使用using语句简化关闭文件的过程。参见MSDN the C# using statement
从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);
}
}
也许这不是您想要的,但是请重新考虑使用异常处理的类型。最初,至少在您将程序员视为用户的情况下,不应将处理视为“用户友好”的。
对此的总结可能是以下文章http://goit-postal.blogspot.com/2007/03/brief-introduction-to-exception.html。
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;
}
我会尝试检查文件。在调用您的读/写并在其中响应用户之前就存在,而不是增加产生错误并稍后捕获错误的开销,因为检查非常容易。我确实知道需要提出错误,但是在这种情况下,简单的检查imho将是更好的解决方案。我的意思是添加另一种方法来检查文件是否存在。
此外,如果您确实要检查文件是否退出,那么您将知道如果无法写入文件,其他原因将阻止该文件。您也可以捕获多个异常,第一个要匹配的异常将被捕获-但您可能知道这一点...