C# 异常处理 详解

总目录


前言


一、异常

1、定义

异常是在程序执行期间出现的问题。

C# 中的异常是对程序运行时出现的特殊情况的一种响应,比如尝试除以零。

异常:程序员必须控制和解决的问题。程序可以正常的运行,但是在运行的过程中出现了问题,问题包括:非法数值,死循环,数组超出索引,访问空对象,格式化转换错误...等等,异常是程序员必须处理的。

异常具有以下属性:

  • 异常是最终全都派生自 System.Exception 的类型。
  • 在可能抛出异常的语句周围使用 try 代码块。
  • 在 try 代码块中出现异常后,控制流会跳转到调用堆栈中任意位置上的首个相关异常处理程序。 在 C# 中,catch 关键字用于定义异常处理程序。
  • 如果给定的异常没有对应的异常处理程序,那么程序会停止执行,并显示错误消息。

2、基本使用

C# 异常处理是建立在四个关键词之上的:try、catch、finally 和 throw。

  • try:try 代码段,表示尝试运行一段代码,如果出现异样,将控制权交给catch。try后可跟一个或多个 catch 块。
  • catch:程序通过异常处理程序捕获异常。catch 关键字表示异常的捕获。
  • finally:finally 块用于执行给定的语句,不管异常是否被抛出都会执行。如:打开一个文件,不管是否出现异常文件都要被关闭。
  • throw:当问题出现时,程序抛出一个异常。使用 throw 关键字来完成。

异常处理格式如下:

csharp 复制代码
	try
	{
 	 	// 引起异常的语句
	}
	catch( ExceptionName e1 ) //try后可以跟一个或多个catch
	{
		throw new Exception("****"); //使用throw 抛出异常信息
	}
	catch( ExceptionName e2 )
	{
   		// 错误处理代码
	}
	finally //finally不是必须的,根据业务需要使用
	{
   		// 要执行的语句 ,不论是否异常都会执行这里的代码
	}

3、异常类

  • System.ApplicationExceptionSystem.SystemException 类是派生于 System.Exception 类的异常类。
  • System.ApplicationException 类支持由应用程序生成的异常。所以程序员定义的异常都应派生自该类。
  • System.SystemException 类是所有预定义的系统异常的基类。

案例代码如下:

csharp 复制代码
        public static void Division(int num1, int num2)
        {
            var result = 0;
            try
            {
                result = num1 / num2;
            }
            catch (DivideByZeroException e)
            {
                Console.WriteLine($"【异常信息】:{e.Message}");
                Console.WriteLine($"【异常代码行】:{e.StackTrace}");
                Console.WriteLine($"【异常所有信息】: {e}");    
            }
            finally
            {
                Console.WriteLine($"Result: {result}");
            }
        }

        static void Main(string[] args)
        {
            Program.Division(18,0);
            Console.ReadLine();
        }

4、自定义异常

定义自己的异常。用户自定义的异常类是派生自 ApplicationException 类。

csharp 复制代码
using System;
namespace UserDefinedException
{
   class TestTemperature
   {
      static void Main(string[] args)
      {
         Temperature temp = new Temperature();
         try
         {
            temp.showTemp();
         }
         catch(TempIsZeroException e)
         {
            Console.WriteLine("TempIsZeroException: {0}", e.Message);
         }
         Console.ReadKey();
      }
   }
}
public class TempIsZeroException: ApplicationException
{
   public TempIsZeroException(string message): base(message)
   {
   }
}
public class Temperature
{
   int temperature = 0;
   public void showTemp()
   {
      if(temperature == 0)
      {
         throw (new TempIsZeroException("Zero Temperature found"));
      }
      else
      {
         Console.WriteLine("Temperature: {0}", temperature);
      }
   }
}

二、异常处理

csharp 复制代码
        static void Main(string[] args)
        {
            Program.Division0(18,0);
            Console.Read();
        }

        public static void Division0(int num1, int num2)
        {
            var result = num1 / num2;
            Console.WriteLine($"Result: {result}");
        }

这是一个存在异常情况的代码,当我们不使用try catch 来捕获异常的时候,运行该程序会直接显示异常,然后关闭程序。

1、不抛出异常

使用try catch 处理异常,程序内部处理掉,不使用 throw 抛出异常。

csharp 复制代码
        static void Main(string[] args)
        {
            Program.Division(18,0);
            Console.Read();
        }

        public static void Division(int num1, int num2)
        {
            var result = 0;
            try
            {
                result = num1 / num2;
            }
            catch (Exception e)
            {
                Console.WriteLine($"【异常信息】: {e}");
            }
            finally
            {
                Console.WriteLine($"Result: {result}");
            }
        }

运行结果:打印错误信息,程序不会因为异常而关闭。

某些场景下,我们需要保证用户的使用体验,选择不抛出异常,大多是上线后,这时基本没什么Bug了

2、抛出异常

使用try catch 处理异常,使用 throw 抛出异常。

csharp 复制代码
        static void Main(string[] args)
        {
            Program.Division(18,0);
            Console.Read();
        }

        public static void Division(int num1, int num2)
        {
            var result = 0;
            try
            {
                result = num1 / num2;
            }
            catch (Exception e)
            {
                Console.WriteLine($"【异常信息】: {e}");
                throw;
            }
            finally
            {
                Console.WriteLine($"Result: {result}");
            }
        }

运行结果:打印错误信息,程序会因为抛出的异常而关闭。

某些场景下,我们需要抛出异常。大多是开发过程中,通过抛出的异常,我们可以更好的定位和修复异常,完善我们开发的程序。

3、throw、throw ex、throw new Exception 的区别

在C#中引发异常的三种常见方法是throw、throw ex 和 throw new Exception(ex),尽管它们看起来很相似,但它们都有不同的行为和实例。

1. 案例

使用throw / throw ex / throw new Exception 抛出异常,然后使用try catch 捕获异常,查看三者抛出的异常信息有什么区别

  • 使用 throw 抛出异常

    运行结果

    使用throw 抛出的异常,再次被捕获的时候,会将错误信息定位在throw 的那一行,而非真正引发异常的 result=num1/num2 那一行。
    如果需要获取,真正的错误信息,就需要在catch中直接先写入日志,再抛出异常,这样有利于我们异常快速定位!

  • 使用throw ex 抛出异常

    运行结果

    使用throw ex 抛出的异常,再次被捕获的时候,会将错误信息定位在throw ex的那一行,而非真正引发异常的 result=num1/num2 那一行。
    如果需要获取,真正的错误信息,就需要在catch中直接先写入日志,再抛出异常,这样有利于我们异常快速定位!

  • 使用throw new Exception 抛出异常

    运行结果

    使用 throw new Exception 抛出的异常,再次被捕获的时候,会有相较于throw 和 throw ex 更为详细的异常信息(列出了导致异常的所有的信息)

2. 进一步 案例

  • 上面的案例仅仅是展示 使用三种形式抛出异常后 运行结果,那么在实际的开发过程中,有可能会出现try catch 嵌套多层的情况。
  • 多层try catch 嵌套的时候就需要注意 异常的追踪
  • 原则上尽量不要嵌套多层,如果嵌套了多层就需要了解这三种抛异常需要注意的点
csharp 复制代码
    internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Program.Division2();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"【捕获抛出的异常】:{ex}");
            }
            Console.ReadLine();
        }


        public static void Division2()
        {
            try
            {
                Program.Division(18, 0);
            }
            catch (Exception ex)
            {
                throw;
                //throw ex;
                //throw new Exception("计算异常", ex);
            }
        }


        public static void Division(int num1, int num2)
        {
            var result = 0;
            try
            {
                result = num1 / num2;
            }
            catch (Exception ex)
            {
                throw;
                //throw ex;
                //throw new Exception("计算异常",ex);
            }
        }
    }
  • 案例代码

  • 使用 throw 抛异常运行的结果

  • 使用throw ex 抛异常运行的结果

    注意:这里通过throw ex 抛出异常,重置堆栈跟踪,将原本行号51 的错误信息,重置到了行号 35,不利于我们定位异常信息。

  • 使用throw new Exception 抛异常的结果

    仍旧是具有最全面的异常信息

3. 小结

三者的区别点:

  • 1、堆栈跟踪

    • throw:保留原始堆栈跟踪,提供有关异常来源的完整信息。
    • throw ex:重置堆栈跟踪,这可能会使调试异常的根本原因发生改变。
    • throw new Exception:为新的异常消息提供其他上下文,同时将原始异常保留为内部异常。
  • 2、应用场景

    • throw:需维护原始异常上下文和堆栈跟踪时,使用该方法。
    • throw ex:该方法谨慎,如需使用,应在重新引发异常之前向异常添加其他信息。
    • throw new Exception:如果需要往异常添加更多上下文,同时保留原始异常详细信息,使用该方式。
  • 3、建议

    • 慎用 throw ex 抛出异常
    • 建议使用throw ex 或者 throw new Exception,使用throw new Exception 内存占用会大些但是信息更全面

结语

回到目录页: C# 知识汇总

希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:
C#文档 - 异常和异常处理
C#异常处理throw、throw ex、throw new Exception 区别
C# 异常处理

相关推荐
@Sunset...5 分钟前
热更新解决方案4——xLua热补丁
unity·c#·lua
陌上花开࿈10 分钟前
用户登录认证
java·开发语言·前端
mingupup16 分钟前
C#继承、多态与封装
c#
小小李程序员30 分钟前
java乱序执行实验
java·开发语言·python
怒放de生命20101 小时前
jenkins 出现 Jenkins: 403 No valid crumb was included in the request
java·servlet·jenkins
筒栗子1 小时前
复习打卡MySQL篇03
android·数据库·mysql
shaoweijava1 小时前
企业车辆管理系统(源码+数据库+报告)
java·数据库·spring boot·mysql
Java&Develop1 小时前
ShardingSphere-多表关联
java·数据库
eternal__day2 小时前
数据结十大排序之(选排,希尔,插排,堆排)
java·数据结构·算法·推荐算法
我焦虑的编程日记2 小时前
【期末复习】JavaEE(上)
java·java-ee