1

我阅读了几篇关于异常处理良好实践的文章。其中大部分解决了作者预期的意外异常。我只是想澄清和消除我可能正在做的不良做法。因为我已经预料到这些问题已经发生了,所以我认为抛出异常有点多余。

假设我有这个代码:

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 通知用户“找不到文件”。有没有一种有效或更好的方法来做到这一点?

我的另一个想法是创建一个包含预期错误代码的枚举,然后创建一个方法,该方法将调用一个 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);
}
4

2 回答 2

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 抛出任何其他类型的异常,它不会被处理。

你可以这样做:

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

在这种情况下,您将创建自己的 Exception 类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
    }
}

但是现在您依靠开发人员检查该值,这曾经是一种常见的方法,但是错误被忽略和忽略了,这就是为什么异常成为更好的实践的原因。

至于向用户显示什么,您真的不应该直接显示异常消息,这些消息是针对开发人员而不是用户的。我建议使用异常对象并返回最佳消息的方法,如下所示:

public string GetUserErrorMessage(Exception ex) {
    if(ex is FileLoadException) {
        var fileLoadException = (FileLoadException)ex;
        return "Sorry but we failed to load the file: " + fileLoadException.FileName;
    }
}

如果愿意,您可以检查异常属性以获取包括错误代码在内的详细信息。此外,我建议在某个地方捕获实际的异常详细信息以供您自己的调试目的,在某个用户看不到的地方。

于 2013-09-28T15:00:59.217 回答
1

这些事情从来都不是一件容易的事。根据定义,一个例外是(如西班牙宗教裁判所)从未预料到的。在您的第一个示例中,当您调用 File.Exist 时文件可能存在,但在您尝试打开它时不存在(文件已删除、网络故障、拇指驱动器被拔出等)。就最佳实践而言:尽可能保护自己,仅捕获您知道如何处理的异常并希望获得最好的结果。程序将失败,您能做的最好的事情是确保尽可能清楚地向代码用户传达未处理的失败(第二个示例的核心)。

传达失败很大程度上取决于代码的作用以及使用它的人。MessageBox 经常被使用,但通常不是“有用的”,因为如果用户不清楚错误的原因,他们唯一能做的就是向您发送 jpg 图像(这是最好的情况 :-D)。提供一种记录问题的方法和/或允许用户剪切/粘贴信息(如堆栈跟踪)的自定义对话框更有用。考虑一下用户选择文件并且出现网络故障的情况。他们告诉你他们收到了关于文件未找到的错误,但是当你或他们看到它时,因为那时网络工作正常。一个简单的“找不到文件”消息确实为您提供了足够的信息,因此您必须告诉用户“我不知道,很高兴它现在可以工作”...... 这并不能激发信心。这里的最佳做法是给自己留下足够的“面包屑”,以便至少对发生的事情有一个粗略的了解。即使是拥有完整文件路径之类的东西也可以为您提供找出问题所在所需的线索。

于 2013-09-28T16:39:28.110 回答