C#中自定义异常类

本文详细介绍了在C#中创建自定义异常类的方法,包括遵循的原则、构造函数的使用,以及如何处理异常信息并将其序列化写入文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C#中自定义异常类

 在C#中所有的异常类型都继承自System.Exception,也就是说,System.Exception是所有异常类的基类. 总起来说,其派生类分为两种:
   1. SystemException类: 所有的CLR提供的异常类型都是由SystemException派生。
   2. ApplicationException类: 由用户程序引发,用于派生自定义的异常类型,一般不直接进行实例化。

   创建自定义异常类应严格遵循几个原则
1. 声明可序列化(用于进行系列化,当然如果你不需要序列化。那么可以不声明为可序列化的)
2. 添加一个默认的构造函数
3. 添加包含message的构造函数
4. 添加一个包含message,及内部异常类型参数的构造函数
5. 添加一个序列化信息相关参数的构造函数.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApplication3
{
    [Serializable] //声明为可序列化的 因为要写入文件中
    public class PayOverflowException : ApplicationException//由用户程序引发,用于派生自定义的异常类型
    {
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public PayOverflowException() { }
        public PayOverflowException(string message)
            : base(message) { }
        public PayOverflowException(string message, Exception inner)
            : base(message, inner) { }
        //public PayOverflowException(System.Runtime.Serialization.SerializationInfo info,
        //    System.Runtime.Serialization.StreamingContext context)
        //    : base(info, context) { }
    }

    internal class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        /// <summary>
        /// current pay
        /// </summary>
        public int CurrPay { get; set; }

        public Employee() { }
        public Employee(int id, string name, int currpay)
        {
            this.ID = id;
            this.Name = name;
            this.CurrPay = currpay;
        }

        /// <summary>
        /// 定义一个GiveBunus的虚方法以供不同的派生类进行重载
        /// </summary>
        /// <param name="amount">奖金额度</param>
        public virtual void GiveBunus(int amount)
        {
            //用一个临时变量记录递增之前的值
            var pay = CurrPay;

            this.CurrPay += amount;

            if (CurrPay > 10000)
            {
                //发生异常,将CurrPay的值进行恢复,
                //并抛出异常,外部程序捕获次异常
                this.CurrPay = pay;
                var ex = new PayOverflowException("The employee's max pay should be no more than 10000.");
                throw ex;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("**** 创建Employee对象,并用try/catch捕获异常 *****");

            var emp = new Employee(10001, "Yilly", 8000);
            try
            {
                emp.GiveBunus(3000);
            }
            catch (PayOverflowException ex)
            {
                Console.WriteLine("异常信息:{0}\n发生于{1}类的{2}方法", ex.Message,
                    ex.TargetSite.DeclaringType, ex.TargetSite.Name);

                try
                {
                    var file = new FileStream(@"c:\customerexception.txt", FileMode.Create);
                    //*** 异常信息写入文件中的代码省略...
                    //以序列化方式写入
                    BinaryFormatter bf = new BinaryFormatter();
                    bf.Serialize(file, ex);
                    file.Close();

                    //以字节方式写入
                    //byte[] buffer = System.Text.Encoding.Default.GetBytes(ex.Message);
                    //int leng = 0;
                    //leng = buffer.GetLength(0);
                    //file.Write(buffer, 0, leng);
                    //file.Close();
                }
                catch (Exception ex1)
                {
                    var inner = new PayOverflowException(ex.Message, ex1);
                    throw inner;
                }
            }

        }
    }
}


值得注意的是:在实例化的时候调用的是PayOverflowException(string message, Exception inner)构造函数,
 如果本程序如果有其他程序在调用的时候, 可以通过.InnerExcetpion的Message属性进行查看内部异常。

转自:http://blog.youkuaiyun.com/wangweitingaabbcc/article/details/6851717

<think>我们将详细讨论C#自定义异常的实现。自定义异常允许我们创建特定于应用程序的错误类型,这些错误类型可以包含额外的错误信息,并且可以更精确地表示程序中发生的特定问题。 ### 自定义异常的实现步骤: 1. **继承自Exception类**:自定义异常类必须直接或间接继承自`System.Exception`类。 2. **提供无参构造函数**:建议提供默认的无参构造函数。 3. **提供带错误信息的构造函数**:通常需要提供接受一个字符串参数的构造函数,用于传递错误消息。 4. **提供带错误信息和内部异常的构造函数**:这允许我们包装内部异常(即导致当前异常的原始异常)。 5. **添加自定义数据**(可选):可以在自定义异常中添加额外的属性,以提供更多关于错误的信息。 ### 示例代码:自定义异常 下面我们创建一个自定义异常`InvalidAgeException`,用于表示年龄无效的错误。该异常将包含一个额外的属性`InvalidValue`来存储无效的年龄值。 ```csharp using System; // 自定义异常类 public class InvalidAgeException : Exception { // 无参构造函数 public InvalidAgeException() { } // 带错误消息的构造函数 public InvalidAgeException(string message) : base(message) { } // 带错误消息和内部异常的构造函数 public InvalidAgeException(string message, Exception inner) : base(message, inner) { } // 添加自定义属性:存储导致异常的无效年龄值 public int? InvalidValue { get; set; } // 可选的:我们可以添加一个构造函数,同时接受错误消息和无效值 public InvalidAgeException(string message, int invalidValue) : base(message) { InvalidValue = invalidValue; } } class Program { static void Main() { try { // 测试自定义异常 Register("John", -5); } catch (InvalidAgeException ex) { Console.WriteLine($"错误: {ex.Message}"); if (ex.InvalidValue.HasValue) { Console.WriteLine($"无效的年龄值: {ex.InvalidValue}"); } } } static void Register(string name, int age) { if (age < 0 || age > 150) { // 抛出自定义异常,并设置InvalidValue属性 throw new InvalidAgeException($"年龄必须在0到150之间。提供的年龄是: {age}", age); } Console.WriteLine($"注册成功!姓名: {name}, 年龄: {age}"); } } ``` ### 代码解释: 1. **自定义异常类`InvalidAgeException`**: - 继承自`Exception`基类。 - 提供了四个构造函数,分别用于不同的场景: - 无参构造函数:用于不提供任何额外信息的情况。 - 带错误消息的构造函数:允许传递错误信息。 - 带错误消息和内部异常的构造函数:允许包装内部异常(用于异常链)。 - 带错误消息和无效年龄值的构造函数:方便同时设置错误消息和无效值。 - 添加了一个可空整数属性`InvalidValue`,用于存储引发异常的无效年龄值。 2. **使用自定义异常**: - 在`Register`方法中,当年龄不在有效范围内时,抛出`InvalidAgeException`异常,并传递错误消息和无效的年龄值。 - 在`Main`方法中,通过`try-catch`块捕获`InvalidAgeException`异常,并输出错误消息和无效值(如果有)。 ### 自定义异常的最佳实践: 1. **命名规范**:异常类名应以“Exception”结尾,如`InvalidAgeException`。 2. **序列化支持**:如果需要在应用程序域之间传递异常(如远程处理或Web服务),则应该实现序列化。这可以通过添加`[Serializable]`特性和实现序列化构造函数来实现。 3. **提供有用的错误信息**:在异常消息中提供足够的信息,以便于诊断问题。 4. **避免深层异常继承**:自定义异常应直接继承自`Exception`或常见的标准异常(如`ArgumentException`),避免过深的继承层次。 ### 添加序列化支持(可选): 如果需要在分布式应用程序中使用自定义异常,应该支持序列化。以下是添加序列化支持的示例: ```csharp using System; using System.Runtime.Serialization; [Serializable] public class InvalidAgeException : Exception { // ... 之前的构造函数 ... // 序列化构造函数(用于反序列化) protected InvalidAgeException(SerializationInfo info, StreamingContext context) : base(info, context) { // 从序列化信息中读取自定义属性 InvalidValue = (int?)info.GetValue(nameof(InvalidValue), typeof(int?)); } // 实现ISerializable接口的GetObjectData方法(用于序列化) public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); // 添加自定义属性到序列化信息中 info.AddValue(nameof(InvalidValue), InvalidValue); } // ... 其他成员 ... } ``` ### 总结: 自定义异常C#中是实现特定错误处理的重要机制。通过创建继承自`Exception`的类,并添加必要的构造函数和自定义属性,我们可以创建富有表现力的错误类型,从而提升代码的可读性和可维护性。 §§相关问题§§: 1. 在C#中,如何实现自定义异常的序列化? 2. 自定义异常应该继承自哪个基类?为什么? 3. 在自定义异常中,如何添加额外的属性? 4. 在C#中,捕获自定义异常与捕获系统内置异常有什么区别?</think>### C# 自定义异常详解 在 C# 中,自定义异常允许你创建特定于应用程序的错误类型,提供更精确的错误信息和额外的上下文数据。以下是完整的实现方案: ```csharp using System; using System.Runtime.Serialization; // 自定义异常基类(推荐实践) [Serializable] public abstract class CustomExceptionBase : Exception { public DateTime Timestamp { get; } = DateTime.UtcNow; protected CustomExceptionBase() { } protected CustomExceptionBase(string message) : base(message) { } protected CustomExceptionBase(string message, Exception inner) : base(message, inner) { } // 序列化支持(跨域传递异常) protected CustomExceptionBase(SerializationInfo info, StreamingContext context) : base(info, context) { Timestamp = info.GetDateTime(nameof(Timestamp)); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue(nameof(Timestamp), Timestamp); } } // 具体业务异常 [Serializable] public class PaymentProcessingException : CustomExceptionBase { public string TransactionId { get; } public decimal Amount { get; } public PaymentProcessingException(string message, string transactionId, decimal amount) : base(message) { TransactionId = transactionId; Amount = amount; } public PaymentProcessingException(string message, string transactionId, decimal amount, Exception inner) : base(message, inner) { TransactionId = transactionId; Amount = amount; } // 序列化构造函数 protected PaymentProcessingException(SerializationInfo info, StreamingContext context) : base(info, context) { TransactionId = info.GetString(nameof(TransactionId))!; Amount = info.GetDecimal(nameof(Amount)); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue(nameof(TransactionId), TransactionId); info.AddValue(nameof(Amount), Amount); } // 重写ToString()提供额外信息 public override string ToString() { return $"{base.ToString()}\nTransaction ID: {TransactionId}\nAmount: {Amount:C}"; } } class PaymentProcessor { public void ProcessPayment(string transactionId, decimal amount) { try { // 模拟支付处理逻辑 if (amount <= 0) throw new PaymentProcessingException( "无效的支付金额", transactionId, amount); if (transactionId.Length != 12) throw new PaymentProcessingException( "交易ID格式错误", transactionId, amount); // 模拟外部API调用 CallExternalPaymentGateway(transactionId, amount); } catch (PaymentProcessingException ex) { // 记录完整异常信息 Console.WriteLine($"支付失败: {ex}\n时间戳: {ex.Timestamp:yyyy-MM-dd HH:mm:ss}"); throw; // 重新抛出给调用者 } } private void CallExternalPaymentGateway(string id, decimal amount) { try { // 模拟API调用失败 throw new TimeoutException("支付网关响应超时"); } catch (Exception ex) { // 包装低级异常 throw new PaymentProcessingException( "支付网关通信失败", id, amount, ex); } } } class Program { static void Main() { var processor = new PaymentProcessor(); try { // 测试有效支付 processor.ProcessPayment("TXN123456789", 100.50m); // 测试无效金额 //processor.ProcessPayment("TXN987654321", -10m); // 测试无效ID //processor.ProcessPayment("SHORT_ID", 50m); } catch (PaymentProcessingException ex) { Console.WriteLine($"捕获支付异常: {ex.Message}"); Console.WriteLine($"内部异常: {ex.InnerException?.Message}"); } catch (Exception ex) { Console.WriteLine($"未处理的异常: {ex}"); } } } ``` ### 代码解释: 1. **自定义异常基类 (`CustomExceptionBase`)** - 继承自 `Exception` 基类 - 添加公共属性 `Timestamp` 记录错误发生时间 - 实现序列化支持(`[Serializable]` 属性和序列化构造函数) - 提供标准异常构造函数重载 2. **具体业务异常 (`PaymentProcessingException`)** - 继承自定义基类 - 添加业务特定属性:`TransactionId` 和 `Amount` - 重写 `ToString()` 提供更丰富的错误信息 - 实现包装构造器:`(string, string, decimal, Exception)` 3. **异常使用模式** - **抛出异常**:使用 `throw new PaymentProcessingException(...)` - **捕获异常**:针对具体异常类型处理 - **异常包装**:捕获低级异常并包装为业务异常 - **异常传播**:使用 `throw;` 保留原始调用栈 4. **序列化支持** - `[Serializable]` 特性标记可序列化 - 实现 `GetObjectData` 方法序列化自定义属性 - 提供序列化构造函数反序列化 ### 自定义异常最佳实践: 1. **命名规范**:以 "Exception" 结尾(如 `PaymentProcessingException`) 2. **继承层次**: ``` System.Exception └── CustomExceptionBase(可选基类) └── PaymentProcessingException(具体异常) ``` 3. **提供上下文**:添加相关业务属性(如交易ID、金额等) 4. **异常包装**:捕获低级异常时使用内部异常构造函数 5. **序列化支持**:确保异常可跨应用域传递 6. **重写ToString**:提供包含所有关键信息的字符串表示 7. **文档说明**:使用XML注释说明异常的使用场景 ### 何时使用自定义异常: - 需要表达特定业务规则违规 - 需要携带额外诊断信息 - 需要区分不同错误来源 - 需要统一处理特定错误类别 - 需要隐藏底层实现细节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值