【问题标题】:C# if exception is caught will it reach my return statement?C# 如果捕获到异常,它会到达我的返回语句吗?
【发布时间】:2013-05-21 22:37:02
【问题描述】:

在 System.IO 中有一个函数: string File.ReadAllText(string path);

我正在尝试编写一个调用 File.ReadAllText 的函数,处理所有可能的异常并返回 true/false 并存储错误消息。

我拥有的是这样的:

public static class FileNoBS
{
    public static bool ReadAllText( string path, out string text, out string errorMessage )
    {
        errorMessage = null;
        text = null;
        bool operationSuccessful = false;

        try
        {
            text = System.IO.File.ReadAllText( path );
            operationSuccessful = true;
        }
        catch ( ArgumentNullException e )
        {
            errorMessage = "Internal software error - argument null exception in FileNoBs.ReadAllText\nMessage: " + e.Message;
        }
        catch ( ArgumentException e )
        {
            errorMessage = "Internal software error - path is a zero-length string, contains only white space, or contains one or more invalid characters as defined by InvalidPathChars in FileNoBs.ReadAllText.\nMessage: " + e.Message;
        }
        catch ( PathTooLongException e )
        {
            errorMessage = "The specified path was too long.\nMessage: " + e.Message;
        }
        catch ( DirectoryNotFoundException e )
        {
            errorMessage = "The specified directory was not found.\nMessage: " + e.Message;
        }
        catch ( FileNotFoundException e )
        {
            errorMessage = "The file specified in path was not found.\nMessage: " + e.Message;
        }
        catch ( IOException e )
        {
            errorMessage = "An I/O error occurred while opening the file.\nMessage: " + e.Message;
        }
        catch ( UnauthorizedAccessException e )
        {
            errorMessage = @"UnauthorizedAccessException

path specified a file that is read-only.
-or-
This operation is not supported on the current platform.
-or-
path specified a directory.
-or-
The caller does not have the required permission.\nMessage: " + e.Message;
        }
        catch ( NotSupportedException e )
        {
            errorMessage = "path is in an invalid format.\nMessage: " + e.Message;
        }
        catch ( SecurityException e )
        {
            errorMessage = "You do not have the required permission.\nMessage: " + e.Message;
        }

        return operationSuccessful;
    }
}

我不明白控制流如何与返回值的函数一起使用。 假设 UnauthorizedAccessException 被捕获,errorMessage 设置为

    errorMessage = "You do not have the required permission..."

我知道 finally 每次都会执行,但编译器不会让我在 finally 块中返回。那么我的回报是否会达到?

另一个问题是如何在遵循官方指南的同时简化这一过程: “一般来说,您应该只捕获那些您知道如何从中恢复的异常。”

我害怕从 File 类(Move、Copy、Delete、ReadAllText、WriteAllText)和 Directory 类中遍历我需要的所有函数,并执行所有这些长代码块只是为了捕获所有我不关心的异常不要抓到太多,因为微软说这很糟糕。

谢谢。

编辑:我得到这样的 cmets 不处理异常这是“别的东西”。

我是我的代码的客户,我想做这样的事情:

if ( !FileNoBS.ReadAllText( path, text, errorMessage ) ) {
   MessageBox.Show( errorMessage );
   return;
}
// continue working with all errors taken care of - don't care for whatever reason file wasn't opened and read, user is notified and I am moving on with my life

【问题讨论】:

  • 您没有处理所有异常,您只是将它们转换为布尔和字符串的“元组”,使客户更难弄清楚发生了什么违反了您尝试的准则遵循“如果您不知道如何处理,请不要处理它们”
  • 我认为您最好在更高级别处理异常并简单地显示异常消息而不是手动设置每条消息。另外,你真的需要返回一个布尔值吗?简单地从文件中返回文本可能会更好。
  • 仍然不太清楚为什么您不喜欢异常的概念,但是您如何使用这样的东西:public static bool ReadAllText(string path, out string text, out string exMsg) { text = null; exMsg = null; try { text = File.ReadAllText(path); return true; } catch (Exception ex) { exMsg = ex.Message; return false; } }

标签: c# exception


【解决方案1】:

由于 try 块或 catch 块中没有 return ,因此将达到您的 return 。

通常,您只想捕获您预期可能发生的异常并有办法处理它们。例如,您可能希望处理从给定路径中找不到的文件,并改为返回默认文件。您应该允许不捕获其他异常,以便您知道发生了意外情况,而不是通过捕获所有异常来隐藏它。

【讨论】:

  • 但这不会让我的程序崩溃吗?此外,我通过向用户显示消息并让他处理来处理所有异常。我当然不会费心修复安全特权或其他任何东西。那是他的工作。如果他在 OpenFileDialog 中选择了要打开的文件,然后在我有机会打开它之前将其删除 - 我应该通知他而不打扰,对吧?
  • @Marko 那些您可以从中恢复的异常,您应该从中恢复。也就是说,如果用户选择了一个不存在的文件,则告诉用户选择另一个文件。只有当您不进行适当的输入验证时,您所听的大多数异常才会发生。例如。如果路径是null
  • 显示消息并没有真正处理它。并且可能存在一些您可以真正处理的错误,只是不在该方法中,而可能在其中一个调用方法中。例如对于“找不到文件”,您不需要显示该错误消息,只需告诉用户选择另一个文件。但如果你真的只是想显示一条消息,那么你可以使用 e.Message() 并添加一个功能,即消息将以用户的语言(UILanguage)显示。
  • 谢谢。这仍然给我留下了 40 多行代码,只是为了显示一条消息(而不是捕获任何不是由 File.ReadAllText 引发的异常)。这就是为什么我不喜欢例外。
【解决方案2】:

是的,它会返回值。

但是,最好在 finally 语句中处理 return 值。

如果在任何情况下都想返回 operationSuccessful 值,那么在 catch 块之后写 finally 块,如下所示,

finally
{
  return operationSuccessful;
}

【讨论】:

  • 不,这不是一个好习惯,这是一个编译错误,因为您无法从 finally 块返回
【解决方案3】:

如果没有发生其他错误,是的。

我会在最后添加:

catch (Exception e)
{
    errormessage = "An unexpected error has occured.";
}

return operationSuccessful;

但是,即使您遇到错误,这也将始终返回成功。我不确定这是否是您想要的,或者您的变量是否命名错误。

【讨论】:

  • 您无法从 finally 中返回,并且捕获您不知道如何处理的异常是不好的做法。捕获所有异常而不处理它们,充其量是“有趣”的调试场景
  • @RuneFS 哎呀,忘记了。虽然你似乎没有像我一样快速加入 Freelancer。
  • 这是真的(因为我还没有读过它)但是在你的情况下,DV 不是为了最终,而是为了提倡捕获所有异常并基本上忽略它们
  • @RuneFS ~ 我的意思是除了特定的之外,最后添加一个来捕捉任何意想不到的期望,而不是这样捕捉所有的期望。这就是为什么我说,“最后”。
  • @RuneFS - 但 OP 特别想要“处理所有可能的异常”。好吧,也许他不应该用“发生意外错误”来做这件事。但e.Message().
【解决方案4】:

正如我在评论中所说,您最好在更高级别处理异常并简单地显示异常消息,而不是手动设置每条消息。我认为在这种情况下,来自异常的消息将具有足够的描述性。

    public static class FileNoBS
    {
        public static string ReadAllText(string path)
        {
            return System.IO.File.ReadAllText( path );
        }   
    }

然后在您的应用程序的某个更高级别上像这样使用它。我通常有一个通用处理程序来处理所有应用程序异常并记录它们并在必要时显示一个消息框。

    try
    {
        var text = FileNoBS.ReadAllText("file.ext");
    }
    catch(Exception e)
    {
    Console.WriteLine(e.Message);
    }

【讨论】:

  • 绝对是最好的方法。我还要补充一点,一些异常可以通过良好的编程来处理——即检查丢失的参数、检查文件或目录是否存在等。
  • ReadAllText 作为ReadAllText 的包装器似乎有点奇怪,但除此之外:+1
  • 我认为这个更高级别的异常处理应该发生在主函数中,在这里我应该在打开文件并从中读取文本并将其显示给用户时发现任何可能出错的错误?
  • 我认为最好从更高级别的功能显示给用户。如果你愿意,在你的 ReadAllText 函数中,你可以通过捕获它然后抛出一个带有更合适消息的异常来向你的异常添加更多信息
【解决方案5】:

您应该尽量避免首先引发这些异常的情况,而不是捕获异常。在您的情况下,您应该在调用 ReadAllText

之前进行一些输入验证
  • 永远不要接受为空的路径 - 你知道这会导致异常,所以在它发生之前处理它
  • 永远不要接受指向不存在文件的路径 - 在调用之前使用 File.Exists(path)
  • 永远不要接受格式错误的路径,例如空字符串或包含无效字符的字符串 - 这将导致异常

这些测试应该在输入源的地方执行。也就是说,如果用户输入它们,请在使用它们之前验证它们。如果它们来自数据库或其他地方,请在使用前验证那里。如果不是用户输入,它们都是系统错误的迹象,应该被视为系统错误,而不是用户应该担心的事情。

预先测试安全异常可能会有些困难,在许多情况下,出现违规是异常的,因此完全可以得到异常。它当然不应该使程序崩溃,而是通过向用户发送错误消息来处理(如果它基于用户输入,如果是系统生成的数据导致了这种情况,则表明系统错误应该在代码级别修复)。在调用发生的地方执行此操作通常比在某些库方法中更合适。

对于IOExceptions,它们可以放入两个桶中。可恢复一次(通常是重试)和不可恢复一次。作为最低要求,向用户提供有关异常的反馈,以便用户可以选择重试。

一个非常普遍的规则应该是纠错逻辑的一部分,那就是永远不要让无效数据在系统中浮动。确保所有对象都管理不变量(有可用的工具,例如代码合同)。当收到来自用户(或其他系统)的无效输入而不是在它们导致异常时拒绝它们。

如果您完成了所有输入验证并且仍然有 E.g. ArgumentNullException 则表示程序逻辑中的错误,您希望能够在测试中轻松找到并在发布错误之前进行更正。你不应该试图掩盖这个错误。

【讨论】:

  • 谢谢。我同意除此之外的所有内容 - “在调用发生的地方执行此操作通常比在某些库方法中更合适。”我想在程序中的一个位置执行此操作,因为如果我从程序中的 10 个位置读取文件,我想用 3 行代码而不是 10 行或 20 行来执行此操作。
  • @m)arko 我不是说你不能在一个地方做到这一点,如果你在十个地方有相同的功能,为什么你要实现 10 次?如果读取文件时功能不同,则根据定义,恢复计划必须不同(否则您仍然可以排除公共部分
  • 我无法预见除了让用户知道发生错误并且无法读取文件之外我还想做其他任何事情的情况。还有什么其他的恢复计划?
  • @marko 你是知道你的应用程序的人。所以我相信你的话。但是,您的代码不会这样做。没有通知用户。您已经使用逻辑包装了一个调用,以将异常转换为布尔值和错误消息。然后,您将在十个不同的地方测试该布尔值并向用户显示错误消息。我是说封装了这十个地方的通用逻辑,包括给用户的通知
【解决方案6】:

return 语句将在代码中出现任何异常的情况下被调用,然后在退出之前将其置于程序的末尾。

我建议放置一个具有高级异常类型的单个异常处理程序,例如“异常”类型本身,并打印或记录异常消息。在每个方法中指定如此多的异常处理程序将花费大量精力,您实际上应该将这些精力投入到方法本身中。

        try
        {
            return ReadAllText("path", "text", "error");
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
        return false;

所以如果方法被调用,它会立即返回,否则异常被打印/记录并且方法将返回 false。

但是,在某些情况下,您可以提及几个或几个显式异常处理程序,您认为这将是有益的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-16
    • 1970-01-01
    • 1970-01-01
    • 2012-11-10
    • 2014-11-06
    • 1970-01-01
    • 1970-01-01
    • 2021-11-23
    相关资源
    最近更新 更多