C#中的throw

Throw会抛出/传递异常,通过在catch块里使用throw语句.可以改变产生的异常,比如我们可以抛出一个新的异常,throw语句有各种各样的,并且很有必要.

例子
我们首先看一下三个方法,分别叫做A,B,C,他们使用不同的throw语句。方法A使用了无参的throw语句。这可以被看作是rethrow(继续抛出)—他会抛出已经出现的同样的异常

继续,方法B throw一个命名的异常变量。这就不是一个完全的rethrow了—因为他虽然抛出了同样的异常。但是改变了StackTrace(堆栈轨迹),如果有必要的话,我们可以收集一些异常信息,而方法C则创建了一个新的异常。
提示:你可以通过这种方法实现自定义的的错误处理
使用throw语句的例子

复制代码
using System;
class Program
{
    static void Main()
    {
    try
    {
        A();
        B();
        C(null);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
    }

    static void A()
    {
    // Rethrow 语法.
    try
    {
        int value = 1 / int.Parse("0");
    }
    catch
    {
        throw;
    }
    }

    static void B()
    {
    // 过滤异常类型.
    try
    {
        int value = 1 / int.Parse("0");
    }
    catch (DivideByZeroException ex)
    {
        throw ex;
    }
    }

    static void C(string value)
    {
    // 创建新的异常.
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    }
}
复制代码

 

程序可能的输出结果

System.DivideByZeroException: Attempted to divide by zero.
System.DivideByZeroException: Attempted to divide by zero.
System.ArgumentNullException: Value cannot be null.
Parameter name: value

 

Rethrow

接着我们看更多的关于rethrows的细节。Rethrow必须是一个无参的throw语句。如果使用throw ex,那么TargetSie(TargetSite 从堆栈跟踪中获取抛出该异常的方法。如果堆栈跟踪为空引用,TargetSite 也返回空引用。-译者注)和StackTrace都被改变了。

在下面的程序里,X()方法使用了rethrow语句。Y()使用了throw ex语句。我们可以看看当rethrow语句使用的使用,引发异常的方法,也就是异常的TargetSite是在StringToNumber---一个int.Parse内部的方法。

但是:当throw ex用的时候。就像在Y()里面,这个异常的TargetSite被修改到了当前的Y()方法里。
测试rethrow的例子

复制代码
using System;

class Program
{
    static void Main()
    {
    try
    {
        X();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.TargetSite);
    }

    try
    {
        Y();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.TargetSite);
    }
    }

    static void X()
    {
    try
    {
        int.Parse("?");
    }
    catch (Exception)
    {
        throw; // [Rethrow 构造]
    }
    }

    static void Y()
    {
    try
    {
        int.Parse("?");
    }
    catch (Exception ex)
    {
        throw ex; // [Throw 捕获的ex变量]
    }
    }
}
复制代码

 

输出

Void StringToNumber(System.String, ...)
Void Y()

 

总结:
异常处理机制提供了可选的控制路径,它将异常逻辑和异常处理分割开来。并且可以通过throw来rethrow异常或是生成一个新的异常。

<think>我们被要求解释C#throw的用法,根据提供的引用材料,我们可以总结如下: 1. throw关键字用于主动引发异常。 2. 有三种常见形式:`throw`、`throw ex`、`throw new Exception(...)` 3. 它们有不同的行为和用途。 我们将从以下几个方面进行说明: ### 1. 基本用法 - `throw`:单独使用,用于重新抛出当前捕获的异常,同时保留原始的异常堆栈信息。 - `throw ex`:重新抛出当前捕获的异常(ex),但会重置异常的堆栈信息,使得异常看起来是从这里开始的(丢失了之前的堆栈)。 - `throw new Exception(...)`:创建一个新的异常实例并抛出。 ### 2. 具体示例与区别 根据引用[1]和[2]: - 在捕获异常后,如果使用`throw`,则异常堆栈会包含最初引发异常的点和重新抛出的点。 - 如果使用`throw ex`,则堆栈信息会从当前抛出点开始,丢失最初引发异常的位置,不利于调试。 - 使用`throw new Exception(...)`可以创建自定义异常,通常用于包装原始异常(作为内部异常)或者抛出全新的异常。 引用[2]中的示例: ```csharp try { // 可能出错的代码 } catch (Exception ex) { throw new Exception("报错", ex); } ``` 这里创建了一个新的异常,并将捕获的异常作为内部异常传递,这样既添加了自定义信息,又保留了原始异常堆栈。 ### 3. 显式抛出异常(引用[3]和[4]) 可以在代码中检测到错误条件时,主动抛出异常。例如: ```csharp if (null == Arg) { throw new ArgumentNullException("Arg"); } ``` 或者像引用[4]中,在switch的default分支抛出异常: ```csharp default: throw new ArgumentOutOfRangeException("不存在的月份"); ``` ### 4. 注意事项 - 在捕获异常后重新抛出时,优先使用`throw;`而不是`throw ex;`,因为后者会破坏堆栈跟踪。 - 创建新的异常时,可以考虑提供更多的错误信息,或者使用更具体的异常类型。 ### 5. 总结 - `throw;`:重新抛出当前异常,保留堆栈。 - `throw ex;`:重新抛出当前异常,但重置堆栈(不推荐)。 - `throw new Exception(...);`:抛出一个新的异常。 下面我们将按照上述思路组织回答。 </think>### C# 中 `throw` 的用法详解 在 C# 中,`throw` 关键字用于主动引发异常,是异常处理的核心机制之一。根据引用内容,其用法主要分为三类: #### 1. **基本 `throw` 语句** - **作用**:重新抛出当前捕获的异常,保留完整的堆栈跟踪信息。 - **使用场景**:需要在更高层级处理异常时(如日志记录),但不破坏原始异常信息。 - **示例**: ```csharp try { /* 可能出错的代码 */ } catch (Exception ex) { // 记录日志后重新抛出 Log(ex.Message); throw; // ✅ 保留原始调用堆栈 } ``` - **关键特性**:不会重置异常的源头信息,调试时可追溯原始错误位置[^1][^2]。 #### 2. **`throw ex`(不推荐)** - **作用**:重新抛出异常,但会重置堆栈跟踪。 - **问题根源**:异常会被标记为从 `throw ex` 处开始,丢失原始错误位置。 - **示例**: ```csharp catch (Exception ex) { throw ex; // ❌ 堆栈跟踪被重置 } ``` - **后果**:增加调试难度,不建议使用[^1]。 #### 3. **`throw new Exception()`** - **作用**:创建并抛出一个全新的异常对象。 - **两种常见形式**: 1. **自定义异常信息**: ```csharp if (input == null) throw new ArgumentNullException("参数不能为空"); ``` *(引用[3]示例)* 2. **包装原始异常**(最佳实践): ```csharp catch (FileNotFoundException ex) { // 添加上下文信息,保留原始异常 throw new CustomException("文件加载失败", ex); } ``` *(引用[2]示例)* - **优势**:可添加辅助信息,同时通过 `InnerException` 保留原始错误[^2][^4]。 --- ### 使用场景对比 | **形式** | **堆栈跟踪保留** | **适用场景** | |----------------------|------------------|----------------------------------| | `throw` | ✅ 完整保留 | 重新抛出当前异常 | | `throw ex` | ❌ 重置 | 应避免使用 | | `throw new Exception`| ✅(需手动附加) | 创建自定义异常或包装原始异常 | --- ### 关键实践建议 1. 优先使用 `throw` 重新抛出异常,确保调试信息完整[^1]。 2. 自定义异常时,通过构造函数传递原始异常(如 `new Exception("msg", ex)`)[^2]。 3. 主动验证参数时直接抛出具体异常类型(如 `ArgumentNullException`)[^3][^4]。 ‡‡ 相关问题 ‡‡ 1. 如何在 C# 中创建自定义异常类型? 2. `throw` 与 `throw ex` 在性能上有何差异? 3. 为什么说 `finally` 块中的 `throw` 可能导致未处理异常? 4. 如何通过异常筛选器(`when`)优化 `throw` 的使用?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值