C#中throw的用法_异常路径

在C#的异常处理中,有一个关键字是throw,大家一定在catch块中或多或少的使用过,那么是否注意过不同的throw语句抛出的异常有何区别,请看下面的例子。

 

例1

class Program
    {
       static void Main(string[] args)
       {
           LoggerFileFactory.ConfigurationFilePath =Path.Combine(AppDomain.CurrentDomain.BaseDirectory,@"Config\log4net.config");
           try
           {
                Class2.Test();
            }
           catch (Exception ex)
           {
               AppLogger.ApplicationLogger.Error("err", ex);
           }
       }
    }
 
   class Class1
    {
       public static void Test()
       {
           throw new Exception("class1");
       }
    }
 
   class Class2
    {
       public static void Test()
       {
           try
           {
                Class1.Test();
           }
           catch (Exception ex)
           {
                //do something
                throw ex;
           }
       }
    }


使用log4net输出的日志如下:

 

例2

下一步,我们将Class2稍作修改

class Class2
    {
       public static void Test()
       {
           try
           {
                Class1.Test();
           }
           catch (Exception ex)
           {
                //do something
                throw;
           }
       }
    }

输出日志如下:

 

例3

我们再来修改下class2,这一次的改动稍微增大

class Class2
    {
       public static void Test()
       {
           try
           {
                Class1.Test();
           }
           catch (Exception ex)
           {
                //do something
                throw newException("class2", ex);
           }
       }
    }



       仔细观察三次的异常日志一定会有所发现,。在例1中,虽然异常的描述信息是class1,但是log4net记录的异常堆栈信息缺没有class1的踪影,只记录下了class2.Test()。而例2中的日志不只记录下了异常的描述信息,而且完整的记录下了引发异常方法的调用信息。

        不同点在哪里,请注意例1中在throw的时候使用了参数ex,而例2没有参数,这个无参的throw可以看做为rethrow(即继续抛出),他会抛出之前捕获到的所有异常。而2中带参数的则不同,他虽然会抛出同样的异常,但是异常的堆栈信息缺在throw的时候被改变了,我们在接下来的try块中无法获取异常的原始信息。例3则完全是一个新的异常,class1中抛出的异常被当做内部异常,存放在innerexception中。

 

         C#为我们提供了相当完备的异常路径,我们可以通过不同的throw方式使异常的路径可选。在实际的使用中需要根据不同的使用场景,选择不同的throw方式。比如需要查看完整的异常路径时,我们应该使用2中的方式,即不带参数的throw。
<think>我们正在讨论C#中如何自定义异常并抛出。根据用户的问题,他想要了解在C#中使用throw抛出异常时如何自定义错误。结合之前的引用内容,特别是引用[2]和[3]提到了自定义异常的创建与使用,以及抛出异常的处理。 解决方案步骤: 1. 创建自定义异常类:通过继承Exception基类来定义自己的异常类型。 2. 在自定义异常类中添必要的构造函数和属性,以传递额外的错误信息。 3. 在需要的地方使用throw关键字抛出自定义异常。 4. 在调用代码中,使用try-catch块捕获并处理这些自定义异常。 下面详细说明每一步: 步骤1:创建自定义异常类 通常,自定义异常类继承自ApplicationException(但更推荐直接继承Exception,因为ApplicationException已被微软建议不再使用[^1])。自定义异常类应至少包含三个构造函数:无参构造函数、带一个字符串参数的构造函数(用于传递错误消息)、带一个字符串参数和一个内部异常参数的构造函数(用于传递错误消息和内部异常)。 步骤2:添额外属性(可选) 如果需要在异常中包含额外的信息(如错误代码、时间戳等),可以在自定义异常类中添属性。 步骤3:抛出自定义异常 在代码中,当检测到错误条件时,使用throw关键字抛出自定义异常的实例。 步骤4:捕获和处理自定义异常 在调用方法中,使用try-catch块捕获自定义异常,并根据需要处理。 示例代码: 定义自定义异常类: ```csharp // 自定义异常类 public class MyCustomException : Exception { // 可以添额外的属性,例如错误代码 public int ErrorCode { get; } // 无参构造函数 public MyCustomException() { } // 带错误消息的构造函数 public MyCustomException(string message) : base(message) { } // 带错误消息和内部异常的构造函数 public MyCustomException(string message, Exception innerException) : base(message, innerException) { } // 自定义构造函数,可以包含错误代码 public MyCustomException(string message, int errorCode) : base(message) { ErrorCode = errorCode; } } ``` 抛出自定义异常: ```csharp public class DataProcessor { public void ProcessData(int data) { if (data < 0) { // 抛出自定义异常,并传入错误消息和错误代码 throw new MyCustomException("数据不能为负数", 1001); } // 正常处理 } } ``` 捕获并处理自定义异常: ```csharp try { DataProcessor processor = new DataProcessor(); processor.ProcessData(-1); } catch (MyCustomException ex) { // 处理自定义异常 Console.WriteLine($"自定义异常:{ex.Message},错误代码:{ex.ErrorCode}"); // 可以记录日志或其他操作 } catch (Exception ex) { // 处理其他异常 Console.WriteLine($"其他异常:{ex.Message}"); } ``` 根据用户引用[3]中的示例,他可能想在一个对象不满足某些接口条件时抛出异常。我们可以这样改写: ```csharp public class ObjShowMe { public static void ShowMe(object obj) { if (!(obj is IFun1 && obj is IFun2)) { // 抛出自定义异常 throw new MyCustomException("对象必须同时实现IFun1和IFun2接口", 2001); } // 正常处理 } } ``` 注意事项: - 自定义异常类名称建议以“Exception”结尾。 - 自定义异常通常用于表示应用程序特定的错误情况。 - 在抛出自定义异常时,应提供有意义的错误消息,以便于调试和维护。 - 尽量避免过度使用自定义异常,只有当系统异常无法准确表达错误时才考虑自定义。 通过自定义异常,我们可以提供更精确的错误信息,并帮助调用者更好地处理错误。 引用说明: 在回答中,我们参考了引用[2]中关于自定义异常的创建与使用的内容,以及引用[3]中抛出异常的处理方式。 相关问题生成: §§相关问题§§ 1. 自定义异常应该继承哪个基类? 2. 如何在自定义异常类中添额外的错误信息? 3. 如何捕获和处理自定义异常? 4. 在什么情况下应该使用自定义异常? </think>### C# 自定义异常的实现方法 在 C# 中使用 `throw` 抛出自定义错误需要以下核心步骤: #### 1. **创建自定义异常类** ```csharp public class CustomException : Exception { // 错误代码(自定义属性) public int ErrorCode { get; } // 基础构造器 public CustomException() { } // 带错误消息的构造器 public CustomException(string message) : base(message) { } // 带错误消息和错误代码的构造器 public CustomException(string message, int errorCode) : base(message) { ErrorCode = errorCode; } // 带错误消息和内部异常的构造器 public CustomException(string message, Exception inner) : base(message, inner) { } } ``` #### 2. **抛出自定义异常** ```csharp public void ProcessData(int value) { if (value < 0) { // 抛出自定义异常 throw new CustomException("数值不能为负数", 1001); } if (value > 100) { // 带内部异常的抛出 var innerEx = new ArgumentOutOfRangeException(nameof(value)); throw new CustomException("数值超过允许范围", innerEx); } // ... } ``` #### 3. **捕获和处理自定义异常** ```csharp try { processor.ProcessData(-5); } catch (CustomException ex) when (ex.ErrorCode == 1001) // 使用条件捕获 { Console.WriteLine($"自定义错误 [代码:{ex.ErrorCode}]: {ex.Message}"); // 记录日志或执行恢复操作 Logger.LogError(ex); } catch (CustomException ex) // 通用捕获 { Console.WriteLine($"其他自定义错误: {ex.Message}"); if (ex.InnerException != null) { Console.WriteLine($"内部异常: {ex.InnerException.Message}"); } } ``` #### 关键要点: 1. **继承关系**:自定义异常必须继承自 `Exception` 或其子类 2. **构造函数**:至少实现标准构造函数(无参数、带字符串参数) 3. **额外属性**:可添自定义属性(如错误代码、时间戳等)存储额外信息 4. **异常链**:通过 `innerException` 参数保留原始异常堆栈[^2] 5. **条件捕获**:使用 `when` 子句实现基于条件的异常处理[^1] #### 最佳实践: - 自定义异常类名后缀使用 `Exception`(如 `ValidationException`) - 为特定领域错误创建专门的异常层次结构 - 在错误消息中包含可操作的解决建议 - 避免在频繁执行的代码路径中抛出异常(影响性能) #### 实际应用示例(基于引用[3]): ```csharp public class InterfaceValidationException : Exception { public Type MissingInterface { get; } public InterfaceValidationException(Type missingInterface) : base($"对象必须实现 {missingInterface.Name} 接口") { MissingInterface = missingInterface; } } public static void ShowMe(object obj) { if (!(obj is IFun1)) throw new InterfaceValidationException(typeof(IFun1)); if (!(obj is IFun2)) throw new InterfaceValidationException(typeof(IFun2)); // 正常处理逻辑 } ``` --- ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值