当前位置: 首页 > news >正文

csharp自定义异常与异常设计建议

1.何时需要自定义异常?

在以下情况下,应该创建自定义异常:

1.业务逻辑错误

标准异常无法准确描述业务错误。需要特定的错误信息和处理逻辑。示例:账户余额不足、订单状态无效等。

2.需要额外的错误信息

标准异常无法提供足够的上下文信息。需要添加自定义属性来存储额外信息。示例:验证失败时需要显示哪些字段有问题。

3.需要特定的异常处理

调用者需要根据异常类型采取不同的处理措施。标准异常类型不够具体。示例:网络错误vs业务错误需要不同的处理。

4.API设计

公开的API需要明确的异常类型。让调用者能够清楚地知道可能发生的错误。示例:库或框架提供的异常类型。

2.创建自定义异常类

自定义异常类必须继承自Exception或其子类

基本结构

C#
public classCustomException : Exception
{
public CustomException() :base()
{
}
public CustomException(string message): base(message)
{
}
public CustomException(string message, Exception innerException)
:base(message, innerException)
{
}
}

C#
/// <summary>
///
表示余额不足时抛出的异常。
/// 包含当前余额和所需金额信息,便于调用方处理。
/// </summary>
public class InsufficientBalanceException : Exception
{
/// <summary>
/// 获取当前账户余额。
/// </summary>
public decimal CurrentBalance { get; }

/// <summary>
/// 获取操作所需的金额。
/// </summary>
public decimal RequiredAmount { get; }

/// <summary>
/// 初始化异常实例,使用当前余额和所需金额构造错误消息。
/// </summary>
/// <param name="currentBalance">当前余额</param>
/// <param name="requiredAmount">所需金额</param>
public InsufficientBalanceException(decimal currentBalance, decimal requiredAmount)
: base($"余额不足。当前余额:{currentBalance},需要:{requiredAmount}")
{
CurrentBalance = currentBalance;
RequiredAmount = requiredAmount;
}

/// <summary>
/// 初始化异常实例,并指定内部异常。
/// </summary>
/// <param name="currentBalance">当前余额</param>
/// <param name="requiredAmount">所需金额</param>
/// <param name="innerException">导致当前异常的原始异常</param>
public InsufficientBalanceException(decimal currentBalance, decimal requiredAmount, Exception innerException)
: base($"余额不足。当前余额:{currentBalance},需要:{requiredAmount}", innerException)
{
CurrentBalance = currentBalance;
RequiredAmount = requiredAmount;
}
}

使用示例

C#
void Withdraw(decimal amount)
{
if (balance < amount){
throw new InsufficientBalanceException(balance, amount);
}
balance-= amount;
}

3.异常命名规范

自定义异常类应该遵循命名规范:

命名规则:类名以Exception结尾。使用描述性的名称,清楚地表达异常的含义。使用 PascalCase 命名。

说明:命名应该清楚地表达异常的含义,让调用者能够理解发生了什么错误。

4.异常设计原则

1.可序列化

如果异常需要在不同进程或机器间传递,应该标记为可序列化。

使用[Serializable]特性。

2.提供有意义的错误消息

错误消息应该清楚地说明发生了什么错误。包含足够的上下文信息。

3.添加自定义属性

如果需要额外的错误信息,添加自定义属性。属性应该是只读的,避免修改。

4.提供多个构造函数

提供无参构造函数。提供带消息的构造函数。提供带消息和内部异常的构造函数。

5.实现序列化支

如果标记为可序列化,需要实现序列化构造函数。实现GetobjectData方法。

5.完整示例:可序列化异常

C#
using System;
using System.Runtime.Serialization;

[Serializable]
public class ValidationException : Exception
{
/// <summary>
///
获取无效的字段名称数组。
/// </summary>
public string[] InvalidFields { get; }

/// <summary>
/// 初始化异常实例,指定无效字段列表。
/// </summary>
/// <param name="invalidFields">无效字段名称数组</param>
public ValidationException(string[] invalidFields)
: base($"验证失败。无效字段:{string.Join(",", invalidFields)}")
{
InvalidFields = invalidFields;
}

/// <summary>
/// 初始化异常实例,指定无效字段列表和内部异常。
/// <summary>
/// <param name="invalidFields">无效字段名称数组</param>
/// <param name="innerException">内部异常</param>
public ValidationException(string[] invalidFields, Exception innerException)
: base($"验证失败。无效字段:{string.Join(",", invalidFields)}", innerException)
{
InvalidFields = invalidFields;
}

/// <summary>
/// 序列化构造函数(用于反序列化)。
/// </summary>
/// <param name="info">序列化信息</param>
/// <param name="context">流上下文</param>
protected ValidationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
InvalidFields = (string[])info.GetValue(nameof(InvalidFields), typeof(string[]));
}

/// <summary>
/// 自定义序列化数据,将 InvalidFields 添加到序列化信息中。
/// </summary>
/// <param name="info">序列化信息</param>
/// <param name="context">流上下文</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue(nameof(InvalidFields), InvalidFields);
}
}

6.异常使用建议

何时使用自定义异常?

业务逻辑错误,标准异常无法准确描述。

需要额外的错误信息。

需要特定的异常处理逻辑。

公开的API需要明确的异常类型。

何时使用标准异常?

参数验证错误:使用 ArgumentException、ArgumentNullException。

对象状态错误:使用InvalidOperationException。

空引用错误:使用NullReferenceException(通常由运行时抛出)。

索引越界:使用IndexOutOfRangeException(通常由运行时抛出)。

选择建议

优先使用标准异常,除非有特殊需求。

自定义异常应该表示特定的业务错误。

避免创建过多的自定义异常类型。

7.异常设计最佳实践

1.保持简单

异常类应该简单,只包含必要的属性和方法。避免在异常类中添加复杂的业务逻辑。

2.提供足够的上下文

错误消息应该包含足够的上下文信息。添加自定义属性存储额外的错误信息。

3.文档化异常

使用XML注释文档化异常。说明异常的使用场景和触发条件。

4.避免过度使用

不要为每个错误都创建自定义异常。优先使用标准异常,除非有特殊需求。

5.考虑异常层次结构

如果多个异常有共同特征,可以创建基类异常。使用继承来组织异常类型。

http://www.cnnetsun.cn/news/3956480.html

相关文章:

  • 2024学术写作工具全测评:从文献管理到格式优化
  • 杰理之开了大于15段的EQ功能后卡音变音的问题【篇】
  • 旁挂负载分担组网场景_分析报告
  • 基于STM32与LoRa的物联网环境检测系统:从硬件选型到低功耗设计
  • 类似WorkBuddy的企业Agent有哪些?主流办公AI Agent选型与深度对比
  • SKILL SELF-EVOLUTION — MICROSOFT SKILLOPT PRINCIPLES (TRAIN SKILLS LIKE WEIGHTS)
  • [VirtualLab] VirtualLab Fusion 中的参数耦合
  • 如何免费让Windows资源管理器拥有毛玻璃效果:ExplorerBlurMica终极美化指南
  • 3个专业技巧让OBS Studio直播画面实现电影级质感:免费色彩校正完整指南
  • 【具身智能】VLA大模型和世界模型有什么区别?
  • 化工AI网:构建产业智能中枢,驱动化工行业数字化转型
  • 不会SQL也能改数据库?我用NocoDB把MySQL变成了表格界面
  • FinalBurn Neo终极指南:轻松打造完美街机模拟体验
  • TRAE Work 与 WorkBuddy 选型决策:基于工作流形态与任务组织的深度对比指南
  • 算力租赁,真正稀缺的到底是什么?
  • 革命性iOS激活锁绕过:applera1n一站式解决方案深度解析
  • 企业智能设备运维管理系统:靠飞算 JavaAI,告别 Java 低效搬砖日常
  • Adobe-GenP:Adobe CC全系列软件激活工具使用指南
  • 社交推荐为什么慢?三度人脉查询的性能排查与图数据库实战
  • 电动汽车参与运行备用的能力评估及其仿真分析(Matlab代码实现)
  • FAQPage Schema 机制分析与 AI 引用率实证研究:5 段问答结构化如何撬动 27.8% 的引用增量
  • 在信号调理中加入Teager-Kaiser能量算子(TKEO)提高了流行的肌电图(EMG)发病检测方法的准确性研究(Matlab代码实现)
  • ChatGPT、Codex实战:MCP接上以后为什么还是不好用?从工具调用、权限到上下文边界的7项排查
  • Agency-Agents 智能体系统从零搭建实战指南
  • Unity中Spine动画精准控制:事件驱动与状态机实践指南
  • Unity数学运算性能优化:从SIMD、Burst到数据导向设计
  • Meta Muse Code、Claude Code、Codex:2026 年三大 AI 编码智能体深度对比
  • Agent Plugins 是什么:跨客户端 AI 插件开放标准,一套配置全平台运行
  • 漏洞挖掘核心技术:从协议解析到智能Fuzzing实战
  • .NET内存管理与性能优化实战