【问题标题】:Proper way to use Try Catch for different situations in C#在 C# 中针对不同情况使用 Try Catch 的正确方法
【发布时间】:2021-10-31 06:31:45
【问题描述】:

我正在使用以下代码,但我不确定正确的方法是什么。

try
{
      // Do code for Try1 
      Console.WriteLine("Try1 Successful");             
}

try
{
      // If try1 didn't work. Do code for Try2
      Console.WriteLine("Try2 Successful");    
}

try
{
      // If try2 didn't work. Do code for Try3
      Console.WriteLine("Try3 Successful");             
}

catch (Exception)
{
      // If try1, 2 and 3 didn't work. print this:
      Console.WriteLine("The program failed");        
}

我想要的是尝试 3 种不同的任务方式,如果其中 3 种失败,则打印“程序失败”,但如果其中一种成功,则不要执行其他方法并继续程序

编辑:

我正在尝试执行的任务是寻找网络路径。

任务 1 将查看路径是否可以打开,如果可以打开目录。

如果不能:任务 2 将查看是否可以打开第二条路径,如果可以打开目录。

如果没有:任务 3 将查看第三条路径是否有效,如果有效,请打开它。

如果不是“在这台电脑上找不到路径”

【问题讨论】:

  • 听起来你希望在第一次捕获中进行第二次尝试,然后在第二次捕获中进行第三次尝试,最后一次捕获将打印失败。像这样try { firstThing; } catch { try { secondThing;} catch{ try { thirdThing; } catch { printFailure; } } }
  • 如果您在设计时知道您的 try1、try2、try3 等代码可能会失败,您应该尝试以向调用者表明它们没有失败的方式编写它们工作。返回一个布尔值以指示成功或失败,或者实现您自己的特定异常类型,从您的 try1 等代码中抛出该异常类型,并仅捕获该异常类型。 catch(Exception) 将捕获(几乎)所有异常类型,并且只应在有限的情况下使用,主要是在应用程序的顶层记录问题所在,而不是让应用程序完全崩溃
  • 如果您可以编辑您的问题以包含您的 try1、try2 等代码正在执行的操作的 minimum reproducible example,我们可以帮助您改进错误处理。
  • 这看起来是一个非常糟糕的设计。我不记得曾经编写或阅读过这样的代码。如果您完全控制被调用的代码,请以不抛出异常的方式设计它。如果您无法访问源代码,请在执行关键代码之前检查前置条件。在你的情况下使用例如Directory.Exists 或 File.Exists 以检查目录/文件是否存在。您渲染的场景可以简单地避免任何异常。异常非常昂贵,并且会大大降低您的应用程序的速度。
  • @sbridewell 由于缺乏信息,我已经投票决定关闭答案。回答这么宽泛的问题是不可能的。你必须知道实际的代码。从来没有一个单一的解决方案对所有情况都是最好的。最佳解决方案可能因情况而异。

标签: c# try-catch


【解决方案1】:

你可以在没有 try catch 块的情况下完成它。 为简单起见,使 Task1、Task2、Task3 具有某种返回类型。 例如,如果它们返回布尔类型。如果任务成功则为 TRUE,如果任务失败则为 FALSE。

或者他们可以返回一些带有布尔结果和字符串错误消息的自定义类型。我不会使用嵌套的 try catch 块。

executeTasks() {

  Console.WriteLine("Try 1");
  if (Task1()) return;

  Console.WriteLine("Try 2");
  if (Task2()) return;

  Console.WriteLine("Try 3");
  if (Task3()) return;

  Console.WriteLine("The program failed");

}

【讨论】:

  • 顶级流程看起来好多了。但请记住,每个 Task# 方法都可以在内部捕获异常并从 catch 块内部重新调整布尔值。这仍然会引入异常性能惩罚。您应该通过验证关键方法调用的先决条件来确保实现每个 Task# 方法以避免引发异常。在这种情况下,该方法可以使用 Directory.Exists("the_path") 并且仅在检查成功时才执行关键代码。调用关键代码并捕获异常以测试提供的路径是否无效是不好的做法。
【解决方案2】:

希望这个 sn-p 可以轻松适应您的需求。

namespace StackOverflow69019117TryCatch
{
    using System;
    using System.IO;

    public class SomeClass
    {
        public void MainMethod()
        {
            var paths = new string[] {
                @"C:\Users\otherUser\Documents", // exists but I don't have access to it
                @"C:\temp", // exists but doesn't contain the folderToSearchFor subfolder
                @"Z:\doesntexist", // doesn't exist
            };

            foreach (var path in paths)
            {
                Console.WriteLine($"Trying with path {path}");
                if (this.ProcessDirectory(path, "folderToSearchFor"))
                {
                    // We've succeeded so exit the loop
                    Console.WriteLine($"Succeeded using path {path}");
                    return;
                }
                else
                {
                    // We've failed so continue round the loop and hope we succeed next time
                    Console.WriteLine($"Failed using path {path}");
                }
            }
        }

        private bool ProcessDirectory(string directoryPath, string folderToSearchFor)
        {
            // First, check whether the directory we want to search actually exists.
            if (!Directory.Exists(directoryPath))
            {
                // Then the directory we're trying to search in doesn't exist.
                // Return false, no need to incur the overhead of an exception.
                Console.WriteLine($"Directory {directoryPath} doesn't exist");
                return false;
            }

            // This doesn't appear to throw an exception if directoryPath isn't accessible to the current user.
            // Instead it just returns whatever the current user has access to (which may be an empty array).
            var propFolderCandidates = Directory.GetDirectories(directoryPath, $"{folderToSearchFor}*");

            // But did it return anything?
            // If not then what we're looking for either doesn't exist or the user doesn't have access to it.
            if (propFolderCandidates.Length == 0)
            {
                // Then there's no folder here matching the search path.
                // Return false, no need to incur the overhead of an exception.
                Console.WriteLine($"Couldn't find folder matching {folderToSearchFor} in {directoryPath}");
                return false;
            }

            var propFolder = propFolderCandidates[0];

            // Consider implementng similar checks in Process.Start.
            // e.g. if it's reading a file, check whether the file exists first
            if (Process.Start(propFolder))
            {
                Console.WriteLine($"Process.Start succeeded using {directoryPath}");
                return true;
            }
            else
            {
                Console.WriteLine($"Process.Start failed using {directoryPath}");
                return false;
            }
        }
    }
}

正如@BionicCode 在各种 cmets 中指出的那样,在执行该操作之前检查一个操作是否可能引发异常要比执行该操作然后处理该操作引发的异常要便宜。

我不得不做一些挖掘来确定当Directory.GetDirectories 尝试获取当前用户无权访问的文件夹的子文件夹时会发生什么 - 我期待它抛出异常,但似乎它没有,它只返回一个空数组,表示当前用户在该位置可以访问的任何内容,因此在这种情况下没有异常处理。

异常的抛出和捕获肯定在 .net 软件中占有一席之地,但如果发生在设计时无法预料的事情,您应该将其视为可以依靠的东西——如果在设计时有办法检测如果某个特定的操作不起作用,那么您应该检测到它并向调用者报告他们请求的操作不起作用,而不是执行该操作并尝试处理它可能引发的任何异常。

Microsoft 就best practice for exceptions 的主题提出了一些明智的建议。

如果事件不经常发生,即如果事件确实异常并指示错误(例如意外的文件结束),则使用异常处理。使用异常处理时,正常情况下执行的代码会更少。

检查代码中的错误情况,如果事件经常发生并且可以被视为正常执行的一部分。当您检查常见的错误条件时,执行的代码会更少,因为您避免了异常。

希望这是有用的:-)

【讨论】:

    【解决方案3】:

    你将不得不嵌套你的 try-catch 块:

    try {
        Console.WriteLine("Try1 successful");
    } catch {
        try {
            Console.WriteLine("Try2 successful");
        } catch {
            try {
                Console.WriteLine("Try3 successful");
            } catch {
                Console.WriteLine("The program failed");
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      我已经找到了一种方法,说实话我不知道这是否是最好的方法,但完全有效。

      解决办法是嵌套几个try-catch(异常)

      这就是我正在做的......

      try
      {
          try
          {
              //Check if PROP  can be found inside initial path A
              string[] PROP_FOLDER = Directory.GetDirectories(Full_PathA, $"{PROP}*");
      
              Process.Start(PROP_FOLDER[0]);
      
              //Open PROP in path A and RETURN
              status_label.Text = "  Found it!";
              status_label.ForeColor = Color.LimeGreen;
          }
          catch (Exception)   //If an error occurs on path A
          {
              try
              {            
                  //Check if PROP  can be found inside initial path B
                  string[] PROP_FOLDER = Directory.GetDirectories(Full_PathB, $"{PROP}*");
      
                  Process.Start(PROP_FOLDER[0]);
                  //Open PROP in path B and RETURN
                  status_label.Text = "  Found it!";
                  status_label.ForeColor = Color.LimeGreen;    
              }
              catch (Exception) //If an error occurs on path B
              {
                  
                  //Check if PROP can be found inside initial path C                     
                  string[] PROP_FOLDER = Directory.GetDirectories(Full_PathC, $"{PROP}*");
      
                  Process.Start(PROP_FOLDER[0]);
                  //Open PROP in path C and RETURN
                  status_label.Text = "  Found it!";
                  status_label.ForeColor = Color.LimeGreen;    
              }
          }
      }
      catch (Exception) //If PROP cannot be found on any of those paths, the PROP doesn't exist
      {
      
          status_label.Text = " Not Found!";
          status_label.ForeColor = Color.Red;
      }
      

      【讨论】:

      • 您问了一个问题,但显然没有阅读 cmets。伤心。不要让你的代码抛出异常!使用静态方法 Directory.Exists() 和 File.Exists。如果他们返回 false,请不要开始该过程。这很简单。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-23
      相关资源
      最近更新 更多