何时使用 try/catch 块?

Posted

技术标签:

【中文标题】何时使用 try/catch 块?【英文标题】:When to use try/catch blocks? 【发布时间】:2010-12-15 22:18:44 【问题描述】:

我已阅读并了解 Try/Catch 块的作用以及使用它的重要性。但我坚持知道 何时/何地 使用它们。有什么建议吗?我将在下面发布我的代码示例,希望有人有时间为我的示例提出一些建议。

    public AMPFileEntity(string filename)
    
        transferFileList tfl = new transferFileList();
        _AMPFlag = tfl.isAMPFile(filename);
        _requiresPGP = tfl.pgpRequired(filename);
        _filename = filename.ToUpper();
        _fullSourcePathAndFilename = ConfigurationSettings.AppSettings.Get("sourcePath") + _filename;
        _fullDestinationPathAndFilename = ConfigurationSettings.AppSettings.Get("FTPStagePath") + _filename;
        _hasBeenPGPdPathAndFilename = ConfigurationSettings.AppSettings.Get("originalsWhichHaveBeenPGPdPath");
    


    public int processFile()
    

        StringBuilder sb = new StringBuilder();
        sb.AppendLine(" ");
        sb.AppendLine("    --------------------------------");
        sb.AppendLine("     Filename: " + _filename);
        sb.AppendLine("     AMPFlag: " + _AMPFlag);
        sb.AppendLine("     Requires PGP: " + _requiresPGP);
        sb.AppendLine("    --------------------------------");
        sb.AppendLine(" ");

        string str = sb.ToString();
        UtilityLogger.LogToFile(str);
        if (_AMPFlag)
        
            if (_requiresPGP == true)
            
                encryptFile();
            
            else
            
                UtilityLogger.LogToFile("This file does not require encryption. Moving file to FTPStage directory.");
                if (File.Exists(_fullDestinationPathAndFilename))
                
                    UtilityLogger.LogToFile(_fullDestinationPathAndFilename + " alreadyexists. Archiving that file.");
                    if (File.Exists(_fullDestinationPathAndFilename + "_archive"))
                    
                        UtilityLogger.LogToFile(_fullDestinationPathAndFilename + "_archive already exists.  Overwriting it.");
                        File.Delete(_fullDestinationPathAndFilename + "_archive");
                    
                    File.Move(_fullDestinationPathAndFilename, _fullDestinationPathAndFilename + "_archive");
                
                File.Move(_fullSourcePathAndFilename, _fullDestinationPathAndFilename);
            
        
        else
        
            UtilityLogger.LogToFile("This file is not an AMP transfer file. Skipping this file.");
        

            return (0);
    


    private int encryptFile()
    

        UtilityLogger.LogToFile("This file requires encryption.  Starting encryption process.");


        // first check for an existing PGPd file in the destination dir.  if exists, archive it - otherwise this one won't save.  it doesn't overwrite.
        string pgpdFilename = _fullDestinationPathAndFilename + ".PGP";



        if(File.Exists(pgpdFilename))
        
            UtilityLogger.LogToFile(pgpdFilename + " already exists in the FTPStage directory.  Archiving that file." );
            if(File.Exists(pgpdFilename + "_archive"))
            
                UtilityLogger.LogToFile(pgpdFilename + "_archive already exists.  Overwriting it."); 
                File.Delete(pgpdFilename + "_archive");
            
            File.Move(pgpdFilename, pgpdFilename + "_archive"); 
        

        Process pProc = new Process();
        pProc.StartInfo.FileName = "pgp.exe";

        string strParams = @"--encrypt " + _fullSourcePathAndFilename + " --recipient infinata --output " + _fullDestinationPathAndFilename + ".PGP";

        UtilityLogger.LogToFile("Encrypting file.  Params: " + strParams);
        pProc.StartInfo.Arguments = strParams;
        pProc.StartInfo.UseShellExecute = false;
        pProc.StartInfo.RedirectStandardOutput = true;
        pProc.Start();
        pProc.WaitForExit();

        //now that it's been PGPd, save the orig in 'hasBeenPGPd' dir
        UtilityLogger.LogToFile("PGP encryption complete.  Moving original unencrypted file to " +  _hasBeenPGPdPathAndFilename); 
        if(File.Exists(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd"))
        
            UtilityLogger.LogToFile(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd already exists.  Overwriting it.");
            File.Delete(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd");
        
            File.Move(_fullSourcePathAndFilename, _hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd");

        return (0);

    

【问题讨论】:

在这里搜索“try catch c#”:***.com/questions/523875/where-to-put-try-catch***.com/questions/751744/thoughts-on-try-catch-blocks***.com/questions/505471/… 等等... 这个网站上有很多关于异常处理的问题! eric lippert 关于捕捉什么的好文章:Fabulous Adventures In Coding: Vexing Exceptions 我知道有一百万个地方我可以阅读异常处理。事实上,我在帖子中提到我已经理解了这个概念。我现在被困在实施中,我的帖子的目的是希望有人向我展示如何将它应用于我的特定情况,我的特定代码示例。 【参考方案1】:

捕获异常的基本经验法则是捕获异常当且仅当您有一种有意义的方式来处理它们

如果您只想记录异常并将其扔到堆栈中,则不要捕获异常。它没有任何意义,而且代码混乱。

当您预计代码的特定部分会失败时,如果您有回退方法,请捕获异常。

当然,您总是遇到需要使用 try/catch 块的已检查异常的情况,在这种情况下您别无选择。即使有已检查的异常,也要确保正确记录并尽可能干净地处理。

【讨论】:

在我看来,记录每个异常是一个好主意,只要你在调用堆栈中执行它并添加一个“throw;”紧随其后的声明。 @Manu - 这并不总是可行的方法。想象一下,你是一个系统管理员,你打开一个日志文件却发现数百条无用的痕迹都说“XXX 中的异常”。这对 TCO 有很大影响,而且通常没有任何好处。登录最合适的位置,以便系统管理员可以使用有意义的跟踪。 IIS 会记录所有抛出的异常,并且有很多解决方案(例如 ELMAH)可以记录所有异常而不会弄乱您的代码。 从源头捕获异常可让您向日志提供元数据,这对于确定异常发生的原因非常有用。处理只是抓住的原因之一;另一个(可以说是更重要的)原因是取证和调试,堆栈跟踪通常只能提供有限的帮助。【参考方案2】:

就像其他人所说的那样,您想在可以抛出 Exception 和您准备处理的代码的代码周围使用 try-catch 块。

对于您的特定示例,File.Delete 可以引发许多异常,例如,IOExceptionUnauthorizedAccessException。在这些情况下,您希望您的应用程序做什么?如果您尝试删除该文件但其他地方的人正在使用它,您将收到IOException

try
    
    File.Delete(pgpdFilename + "_archive")

catch(IOException)

    UtilityLogger.LogToFile("File is in use, could not overwrite.");
   //do something else meaningful to your application
   //perhaps save it under a different name or something

另外,请记住,如果这确实失败了,那么你在你的 if 块之外执行的 File.Move 也将失败(再次到 IOException - 因为文件没有被删除它仍然存在这将导致移动失败)。

【讨论】:

【参考方案3】:

我被教导将 try/catch/finally 用于可能发生多个错误并且您可以实际处理的任何方法/类。数据库事务、FileSystem I/O、流等。核心逻辑通常不需要try/catch/finally。

try/catch/finally 的重要之处在于您可以有多个捕获,以便您可以创建一系列异常处理程序来处理 非常 特定错误或使用一般异常来捕获任何内容您看不到的错误。

在您的情况下,您使用的 File.Exists 很好,但它们可能是磁盘的另一个问题,可能引发 File.Exists 无法处理的另一个错误。是的,它是一个布尔方法,但是说文件被锁定了,如果你尝试写入它会发生什么?使用 catch,您可以计划一个罕见的场景,但如果没有 try/catch/finally,您可能会将代码暴露在完全无法预料的情况下。

【讨论】:

这里所说的某些内容是不正确的 - 例如,尝试捕获 FileIO 的异常总是有用的 - 它允许开发人员通知用户,例如,可能没有所需驱动器上有足够的空间来完成保存操作,或者文件不存在(如果开发人员没有正确编码检查......遗憾的是我已经看到了)。 @Paul 如果您可以将其编码为异常,您也可以将其编码为函数开头的要求。因此,问题不在于您是否必须使用异常,而在于是否没有其他选择。异常也不总是使代码更清晰,因为您正在将处理部分从错误中移开。【参考方案4】:

其他人给出了很多很好的指导和参考。

我的输入很短: 何时使用它是一回事,同样或更重要的是如何正确使用它。

PS:“它”是对“试图捕获异常”的引用。

【讨论】:

以上是关于何时使用 try/catch 块?的主要内容,如果未能解决你的问题,请参考以下文章

try-catch的使用以及细节

sqlConnection/Command 使用语句 + try/catch 块 [重复]

如何在 PHP 中有效地使用 try...catch 块

Node.JS - 无法使用try / catch块获得异步抛出

使用 mockito [Spring-Boot] [重复] 测试 void 函数 try/catch 块

Sql语法高级应用之六:如何在Sql语句中如何使用TRY...CATCH