我阅读了几篇关于异常处理的良好做法的文章。其中大部分内容都解决了作者所期望的意外异常。我只想澄清并消除我可能正在做的可能的不良做法。由于我已经预料到这些问题已经发生,我认为抛出异常有点多余。
假设我有这段代码:
string fileName = Path.Combine(Application.StartupPath, "sometextfile.txt");
// There's a possibility that the file doesn't exist <<<<<<<<<<<<<<<<<<<<<
if (!File.Exists(fileName))
{
// Do something here
return;
}
// Therefore, this will return an exception
using (StreamReader file =
new StreamReader(fileName))
{
// Some code here
}
当然,我要做的是通过MessageBox通知用户说“找不到文件”。是否有效或更好的方法?
我的另一个想法是创建一个包含预期错误代码的 enum ,然后创建一个方法,该方法将调用MessageBox显示该特定情况的错误消息:
enum ErrorCodes {null, zero, ...}
public void showError(ErrorCodes error)
{
string message;
switch (error)
{
case ErrorCode.null:
{
message = "value cannot be null";
break;
}
case ErrorCode.zero:
{
message = "cannot divide by zero";
break;
}
}
MessageBox.Show(message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
答案 0 :(得分:1)
如果尝试访问该文件的代码位于前端,例如点击的事件处理程序,则可以检查错误情况,显示消息并返回。
如果我理解你的问题,你想知道你是否应该这样做:
public void button_Click() {
if(!File.Exists(textBox.Text)) {
MessageBox.Show("Could not find the file");
return;
}
ProcessFile(textBox.Text); // would have thrown an exception if the file didn't exist
}
这没关系,除非ProcessFile抛出任何其他类型的Exception,否则它将无法处理。
你可以这样做:
public void button_Click() {
try {
ProcessFile(textBox.Text); // throwns an exception if the file didn't exist
} catch(Exception ex) {
MessageBox.Show(GetUserMessage(ex));
return;
}
}
在我看来,两者都做得更好:
public void button_Click() {
try {
if(!File.Exists(textBox.Text)) {
MessageBox.Show("Could not find the file");
return;
}
ProcessFile(textBox.Text); // throwns an exception if the file didn't exist
} catch(Exception ex) {
MessageBox.Show(GetUserMessage(ex));
return;
}
}
通过这种方式,您可以向用户提供与此时相关的最具体信息。例如,如果他试图打开Excel文件,您可以说“无法找到您要导入的Excel文件”。
这也适用于在您检查的点和您尝试处理文件的点之间删除或重命名文件的情况。
或者你可以用以下方法完成类似的事情:
public void button_Click() {
try {
if(!File.Exists(textBox.Text)) {
throw new UserException("Could not find the file");
}
ProcessFile(textBox.Text); // throwns an exception if the file didn't exist
} catch(Exception ex) {
MessageBox.Show(GetUserMessage(ex));
return;
}
}
在这种情况下,您将创建自己的异常类UserException
并只传递该消息而不进行翻译。这将允许您重复使用用于显示消息的相同代码。
课程中的例外
如果某些类库中发生错误,则应抛出异常。异常的目的是不会忽视错误。
例如,您不应该这样:
class MyFileHandler {
public void OpenFile(string fileName) {
if(!File.Exists(fileName)) return;
// do stuff
}
public void DoStuff() {
// do stuff
}
}
现在,如果开发人员称myFileHandlerInstance.OpenFile("note.txt")
,他会认为它有效。你可以返回一个布尔值,如下所示:
class MyFileHandler {
public bool OpenFile(string fileName) {
if(!File.Exists(fileName)) return false;
// do stuff
return true;
}
public void DoStuff() {
// do stuff
}
}
但是现在你依赖于开发人员检查这个值,这曾经是一种常见的方法,但错误被忽略和忽略,这就是为什么Exceptions变得更好的做法。
至于要向用户显示的内容,您实际上不应该直接显示Exception消息,这些消息是供开发人员而非用户使用的。我建议一个方法,它接受一个异常对象并返回最好的消息,如下所示:
public string GetUserErrorMessage(Exception ex) {
if(ex is FileLoadException) {
var fileLoadException = (FileLoadException)ex;
return "Sorry but we failed to load the file: " + fileLoadException.FileName;
}
}
如果您愿意,可以检查Exception属性以获取包括错误代码在内的详细信息。此外,我建议在某个地方捕获实际的异常详细信息,以用于您自己的调试目的,在某些地方,用户看不到它。
答案 1 :(得分:1)
这些事情从来都不容易。根据定义,例外(如西班牙宗教裁判所)从未预料到。在您的第一个示例中,当您调用File.Exist时,该文件可能存在,但在您尝试打开它时不存在(文件已删除,网络失败,拉动拇指驱动器等)。至于最佳做法:尽可能保护自己,只捕捉你知道如何处理的例外,并希望最好。程序将失败,您可以做的最好的事情是确保未处理的故障尽可能清楚地传达给代码的用户(第二个示例的核心)。
沟通失败在很大程度上取决于代码的作用和使用者。通常使用MessageBoxes但通常不“有用”,因为如果用户不清楚错误的原因,他们唯一可以做的就是向你发送jpg图像(这是最好的情况:-D)。提供一种记录问题的方法和/或允许用户剪切/粘贴信息的自定义对话框(如堆栈跟踪)更有用。想想用户选择文件并且存在网络打嗝的情况。他们告诉你他们得到了关于找不到文件的错误,但当你或他们看到它时,因为那时网络工作正常。一个简单的“找不到文件”消息确实给你足够的信息,所以你必须告诉用户“我不知道,很高兴它现在有效”......这并不能激发你的信心。这里的最佳做法是给自己留下足够的“面包屑”,至少对发生的事情有一个粗略的了解。即使是拥有完整文件路径之类的东西,也可以为您找出解决问题所需的线索。