重庆熊猫 Loading

C#教程 - 异常处理(Exception Handling)

更新记录
转载请注明出处:
2022年9月20日 发布。
2022年9月10日 从笔记迁移到博客。

异常处理(Exception Handling)

说明

异常是运行时的错误,当系统捕获错误后将会抛出异常

C# exceptions are represented by classes

异常的类型:

程序违反了系统约束或应用程序约束

程序开发人员产生的BUG

用户的错误输入、不正常的操作

程序运行中的不可预知、非预料的错误

异常处理与其他错误处理对比

与方法返回错误代码对比:

​ 方法可能会嵌套几十层,使用返回错误代码,方法需要走过几十层才可以退出

​ 嵌套的异常处理可以立即被异常捕获代码处理

​ 方法返回错误代码在代码可读性上不如异常处理

​ 异常使用对象处理错误,表达错误信息更加方便

异常性能问题

异常处理的性能不如普通代码

常见可以使用代码解决的错误,尽量不要使用异常处理错误

常见替代方法:

字符串转为数字,使用TryParse而不是Parse

验证用户输入的内容

​ 使用is和as以避免抛出InvalidCastException异常

Try-Catch-Finally语句(Try Statements and Exceptions)

作用:错误处理(error-handling) 或 清理无用内存(cleanup code)

在try块内放置可能出现异常而被保护代码段

在catch子句内放置处理异常的代码段(可以有多个),也可以再次抛出异常

在finally子句放置所有情况下都要执行的代码

注意:唯一能让finally块不执行的是无限循环或进程突然结束

使用throw抛出异常

如果try块内的代码没有发生异常,那么将跳过catch块,直接执行finally

如果try块中有return语句或者在catch中抛出异常,finally块也总会执行

image

catch的三种形式

image

实例:catch不带变量名称

catch (OverflowException) // no variable
{
    // ...
}

实例:catch不带类型

catch 
{ 
    //... 
}

多catch情况下catch的排序

image

注意:子异常在前,父异常在后

异常过滤(Exception Filters)

添加when语句就可以了,语句内放置表达式

实例:异常过滤

catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout)
{
    //...
}

实例:多个catch块

catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout)
{ 
    //... 
}
catch (WebException ex) when (ex.Status == WebExceptionStatus.SendFailure)
{ 
    //...
}

finally块执行过程

image

抛出异常

使用throw语句抛出异常

语法:

throw ExceptionObject;      //抛出异常对象
throw;                      //只能在catch中使用,表示再次抛出该错误

注意:

​ 不带异常对象的throw只可以在catch块中使用,这种形式会重新抛出当前异常,继续按调用栈顺序去搜索它的处理代码

thorw可以和表示式体结合使用

实例:

public string Foo() => throw new NotImplementedException();

再次抛出异常(Rethrowing an exception)

实例:再次抛出相同的异常

try { ... }
catch (Exception ex)
{
    // Log error
    //...
    throw; // Rethrow same exception
}

注意:

如果将throw替换为throw ex仍然可以工作

但是新传播的异常的StackTrace属性将不再反映原始错误

实例:再次抛出不一样类型的异常

try
{
    //... // Parse a DateTime from XML element data
}
catch (FormatException ex)
{
    throw new XmlException ("Invalid DateTime", ex);
}

捕获异常

如果异常发生了没有被捕获,会按调用栈顺序继续向上抛出异常,直到被catch捕获,如果没有catch捕获,将导致程序终止
image

try-catch-finally执行过程示意图

image

再次抛出异常

try {  ...  }
catch (Exception ex)
{
  // Log error
  ...
  throw;          // Rethrow same exception
}

注意:

If we replaced throw with throw ex, the example would still work, but the StackTrace property of the newly propagated exception would no longer reflect the original error.

.NET中预定义异常

说明

The exception classes in C# are mainly directly or indirectly derived from the System.Exception class

Some of the exception classes derived from the System.Exception class are the System.ApplicationException and System.SystemException classes

The System.ApplicationException class supports exceptions generated by application programs

The System.SystemException class is the base class for all predefined system exception.

应用最好自定义一个自己应用的基类异常

预定义异常层次结构

image

注意:用户应用异常最好继承自AplicationException

常见预定义异常对象

System.Exception;          //最基本的异常类
System.ArgumentException;      //参数错误异常
System.ArgumentNullException;    //参数为Null错误异常
System.ArgumentOutOfRangeException   //参数超出异常
System.ApplicationException;     //应用异常
System.FormatException;       //参数格式错误异常
System.IndexOutOfRangeException;  //越界异常
System.InvalidCastException;     //无效转换异常
System.InvalidOperationException
System.StackOverflowException;   //深嵌套堆栈溢出异常
System.NullReferenceException;   //空引用对象异常
System.ArithmeticException;      //数值运算异常
System.IO.IOException                  //I/O 错误处理
System.NotSupportedException
System.NotImplementedException
System.ObjectDisposedException
System.ArrayTypeMismatchException; //数组元素类型匹配错误异常

image

异常筛选(Exception filters)

说明

从C# 6.0开始可以定义异常筛选

使用when子句即可

实例

实例1:

catch (WebException ex) 
when (ex.Status == WebExceptionStatus.Timeout)
{
  ...
}

实例2:

catch (WebException ex) when (ex.Status == something)
{ ... }
catch (WebException ex) when (ex.Status == somethingelse)
{ ... }

Exception异常对象

说明

所有异常类继承自System.Exception类

System.Exception对象常用成员:

属性 类型 描述
Message String 异常描述字符串
Data 给异常添加键值对描述
HelpLink String 异常帮助链接
InnerException Exception 如果当前异常是另一个异常引起的,这个属性包含前一个异常的引用
StackTrace String 描述异常在调用栈上信息,有助于跟踪抛出异常的方法
Source String 导致异常的应用程序名或对象名
HResult 分配给异常的一个数值
TargetSite .NET反射对象,描述了抛出异常的方法

实例-给Data添加键值对

Exception ex = new Exception("Panda Test");
//使用方法添加
ex.Data.Add("Panda", 666);
//使用索引添加
ex.Data["Dog"] = 888;

实例-自定义异常

规范:

​ 异常类以Exception结尾

​ 在Visual Studio中使用exception双tab生成标准异常

[Serializable]
public class MyException : Exception
{
    public  MyException() { }
    public  MyException(string message) : base(message) { }
    public  MyException(string message, Exception inner) : base(message, inner) { }
    protected  MyException(
      System.Runtime.Serialization.SerializationInfo info,
      System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}

实例-再次抛出异常(原异常)

using System;

namespace Test
{
    class TestClass
    {
        public static void Main()
        {

            try
            {
                //Some code
                try
                {
                    //Some code
                    throw new Exception("InnerException");
                }
                catch (Exception e)
                {
                    //Slove Error

                    //再次抛出异常
                    throw e;
                }
            }
            catch(Exception e)
            {
                //Slove Error
            }

            Console.ReadKey();
        }
    }
}

实例-再次抛出异常(新异常)

using System;

namespace Test
{
    class TestClass
    {
        public static void Main()
        {

            try
            {
                //Some code
                try
                {
                    //Some code
                    throw new Exception("InnerException");
                }
                catch (Exception e)
                {
                    //Slove Error

                    //再次抛出异常
                    throw new Exception("OtherException");
                }
            }
            catch(Exception e)
            {
                //Slove Error
            }

            Console.ReadKey();
        }
    }
}

捕捉平台不支持API异常

使用PlatformNotSupportedException异常类型

try
{
    Console.WindowWidth = int.Parse(args[2]);
    Console.WindowHeight = int.Parse(args[3]);
}
catch (PlatformNotSupportedException e)
{
    Console.WriteLine("The current platform does not support changing the size of a console window.");
    Console.WriteLine(e.Message);
}

捕捉格式错误异常

try
{
    //do something
}
catch (FormatException)
{
    Console.WriteLine("The age you entered is not a valid number format.");
}
catch (Exception ex)
{

}

捕捉除以零异常

整数类型和decimal类型除以零会发生异常

注意:浮点数(double、float)除以零不会发生异常

try 
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(5 / i);
    }
}
catch(DivideByZeroException e)
{
    Console.WriteLine(e.Message);
    //Attempted to divide by zero.
}

捕捉所有异常

try
{
    //do something
}
catch (Exception ex)
{
    Console.WriteLine($"{ex.GetType()} says {ex.Message}");
}

异常最佳实践

抛出具体的明确的Exception的子类

不要直接抛出System.SystemException异常,可以继承该类

不要直接抛出Exception, NullReferenceException, ApplicationException

可以直接调用System.Environment.FailFast()方法,当无法处理程序错误的情况下

在catch block中使用直接throw,而不是再次抛出其他类型的异常

posted @ 2022-09-20 08:11  重庆熊猫  阅读(1104)  评论(0编辑  收藏  举报