C#嵌套的Try Catch语句或方法?

17

简单的最佳实践问题。

你应该嵌套try catch语句还是只使用方法呢?

例如,如果您有一个打开文件,执行操作并关闭文件的方法,您应该将打开和关闭放在try catch外面,或者将关闭放在finally块中。

现在,如果打开文件的方法失败了,该方法会断言,对吧?那么,你应该将其包装在try catch块中呢,还是应该从另一个方法调用它,而该方法反过来具有try catch块呢?


示例代码将有助于说明您的问题。 - Alan
有没有任何示例(最好是真实的)代码来说明问题? - Kiquenet
8个回答

15

在打开文件的方法中,我会使用using语句而不是try catch。using语句可以确保如果发生异常时Dispose方法被调用。

using (FileStream fs = new FileStream(file, FileMode.Open))
{
    //do stuff
}

执行的操作与以下代码相同:

FileStream fs;
try
{
     fs = new FileStream(file, FileMode.Open);
     //do Stuff
 }
 finally
 {
        if(fs!=null)
           fs.Dispose();
 }

3
准确但try/finally代码与using代码不完全相同。你还需要将其包装在一个匿名作用域块中。 - Joel Coehoorn

12

现在我们有了lambda表达式、类型推断和其他一些东西,这使得另外一些语言中常见的习惯用法在C#中也很有意义。你的例子是关于打开一个文件,对它进行一些操作,然后关闭它。现在,你可以编写一个帮助方法来打开文件,并且负责确保关闭/释放/清理文件,但是调用你提供的lambda表达式来执行“操作”部分。这将帮助您在一个地方正确地处理复杂的try/catch/finally dispose/cleanup事项,然后反复使用。

以下是示例:

public static void ProcessFile(string filePath, Action<File> fileProcessor)
{
  File openFile = null;

  try
  {
    openFile = File.Open(filePath); // I'm making this up ... point is you are acquiring a resource that needs to be cleaned up after.

    fileProcessor(openFile); 
  }
  finally
  {
    openFile.Close(); // Or dispose, or whatever.
  }
}

现在,调用这个方法的人不必担心如何打开文件或关闭/释放它。他们可以像这样做:

Helpers.ProcessFile("C://somefile.txt", f => 
 {
   while(var text = f.ReadLine())
   {
     Console.WriteLine(text);
   }
 });

@alhambraeidos,如果我理解正确的话,是的,你绝对可以这样做。你可以创建一个名为“ForEachLine”的方法,它打开文件,使用while循环逐行读取文件,并在每一行上调用传入的函数。 - Charlie Flowers
像这样:MyHelperClass4Files.ForEachLine(f); - Kiquenet
像这样:Helpers.ForEachLine("@c:\somefile.txt", aLine => Console.WriteLine("下一行说: " + aLine); 这将把文件中的所有行输出到控制台,每行前面都有“下一行说这个:”,它还将确保文件被正确关闭。 - Charlie Flowers

7

这是一个关于代码风格的问题,但对我来说,我尽量避免在单个方法中嵌套超过一层的try/catch/finally语句。当你遇到嵌套的try时,几乎肯定已经违反了“一个函数=一个操作”原则,应该使用第二个方法。


5
我认为在许多开发者中,如果不是大多数的话,这不是一个风格问题。这是一个不理解.NET中异常处理的问题,以及认为他们需要像Java一样捕获每个异常的问题。在大多数情况下,最好只是让异常传播。 - John Saunders

4

具体取决于您要做什么,但在大多数情况下,嵌套的try/catch表示函数过于复杂(或程序员不太了解异常的工作原理!)。

对于打开文件的情况,我会使用一个IDisposable持有者和using语句,从而避免任何显式的try/catch。


3

如果您有相关的代码,但这些代码并不一定属于单独的函数,那么这种情况下是否正确呢?

try
{
  // Part 1 Code Here

  try
  {
    // Part 2 Code Here
  }
  catch (Exception ex)
  {
    // Error from Part 2
  }
}
catch (Exception ex) 
{
  // Error from Part 1
} 

1
大多数情况下,我会将嵌套的 try/catch 块拆分成函数。但有时我会编写代码来捕获并记录应用程序抛出的所有未捕获异常。但如果记录代码失败怎么办?因此,我又在周围加了一个 try/catch,以防止用户看到默认的 .NET 未处理异常对话框。但即使这段代码也很容易重构为函数,而不是嵌套的 try/catch 块。
try
{
    try
    {
        DoEverything(); 
    }
    catch (Exception ex)
    {
        // Log the exception here
    }
}
catch (Exception ex)
{
    // Wow, even the log is broken ...
}

1
如果您的日志记录出现问题,那么您应该让异常传播,以便用户可以抱怨您的日志记录出了问题! - John Saunders
我仍然可以通过日志代码显示抛出的异常。只是我想自己处理,而不是让框架错误窗口弹出。 - Brian Ensink

0
//create a switch here and set it to 0 
try
{
    DoChunk1(); 
    //looks good. set the switch to 1
}
catch (Exception ex)
{
    // Log the exception here
}

// 检查开关,如果此时仍为零,则可以在此处停止程序;否则将开关设置回零并执行下一个try catch语句。完全同意按照上述方式分解它们

try { DoChunk2(); //看起来不错。将开关设置为1 } catch (Exception ex) { // 在此处记录异常 }


-2
try 
{
  ----
}
catch
{
   try
      {
           ---
      }
   catch
      {
        ---
      }
}

1
@choopage-JekBao 可能是因为它没有支持的上下文来解释为什么这应该成为被接受的答案。简短的描述/代码注释不会有任何影响。 - Phil Cooper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接