自定义异常 #
一、为什么需要自定义异常 #
1.1 自定义异常的好处 #
- 表达特定业务错误
- 提供更详细的错误信息
- 区分不同类型的错误
- 支持特定错误处理
1.2 何时创建自定义异常 #
- 业务逻辑错误
- 特定领域错误
- 需要额外错误信息
- 需要特殊处理
二、创建自定义异常 #
2.1 基本自定义异常 #
csharp
public class BusinessException : Exception
{
public BusinessException() { }
public BusinessException(string message) : base(message) { }
public BusinessException(string message, Exception innerException)
: base(message, innerException) { }
}
2.2 带额外属性的自定义异常 #
csharp
public class ValidationException : Exception
{
public string PropertyName { get; }
public object AttemptedValue { get; }
public ValidationException(string propertyName, object attemptedValue, string message)
: base(message)
{
PropertyName = propertyName;
AttemptedValue = attemptedValue;
}
public ValidationException(string propertyName, object attemptedValue, string message, Exception innerException)
: base(message, innerException)
{
PropertyName = propertyName;
AttemptedValue = attemptedValue;
}
}
2.3 带错误码的自定义异常 #
csharp
public class ApiException : Exception
{
public int ErrorCode { get; }
public string ErrorDetails { get; }
public ApiException(int errorCode, string message)
: base(message)
{
ErrorCode = errorCode;
}
public ApiException(int errorCode, string message, string details)
: base(message)
{
ErrorCode = errorCode;
ErrorDetails = details;
}
public override string ToString()
{
return $"[{ErrorCode}] {Message}\n{ErrorDetails}";
}
}
2.4 序列化支持 #
csharp
[Serializable]
public class DomainException : Exception
{
public string Domain { get; }
public DomainException() { }
public DomainException(string message) : base(message) { }
public DomainException(string message, Exception innerException)
: base(message, innerException) { }
public DomainException(string domain, string message)
: base(message)
{
Domain = domain;
}
protected DomainException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
Domain = info.GetString(nameof(Domain));
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue(nameof(Domain), Domain);
}
}
三、使用自定义异常 #
3.1 抛出异常 #
csharp
public class UserService
{
public void Register(string username, string password)
{
if (string.IsNullOrEmpty(username))
throw new ValidationException("username", username, "用户名不能为空");
if (password.Length < 6)
throw new ValidationException("password", password, "密码长度不能少于6位");
if (_userRepository.Exists(username))
throw new BusinessException("用户名已存在");
}
}
3.2 捕获异常 #
csharp
try
{
_userService.Register(username, password);
}
catch (ValidationException ex)
{
Console.WriteLine($"验证失败:{ex.PropertyName} - {ex.Message}");
}
catch (BusinessException ex)
{
Console.WriteLine($"业务错误:{ex.Message}");
}
3.3 异常链 #
csharp
public class OrderService
{
public void ProcessOrder(int orderId)
{
try
{
var order = _orderRepository.GetById(orderId);
ValidateOrder(order);
ProcessPayment(order);
}
catch (PaymentException ex)
{
throw new OrderProcessingException("订单处理失败", ex);
}
}
}
四、常见自定义异常示例 #
4.1 验证异常 #
csharp
public class EntityValidationException : Exception
{
public Type EntityType { get; }
public IDictionary<string, string[]> ValidationErrors { get; }
public EntityValidationException(Type entityType, IDictionary<string, string[]> errors)
: base($"验证失败:{entityType.Name}")
{
EntityType = entityType;
ValidationErrors = errors;
}
public override string Message
{
get
{
var errors = ValidationErrors.SelectMany(kvp =>
kvp.Value.Select(v => $"{kvp.Key}: {v}"));
return $"{base.Message}\n{string.Join("\n", errors)}";
}
}
}
4.2 配置异常 #
csharp
public class ConfigurationMissingException : Exception
{
public string Key { get; }
public string Section { get; }
public ConfigurationMissingException(string key, string section = null)
: base($"配置项缺失:{key}")
{
Key = key;
Section = section;
}
}
4.3 并发异常 #
csharp
public class ConcurrencyException : Exception
{
public string EntityName { get; }
public object EntityId { get; }
public int ExpectedVersion { get; }
public int ActualVersion { get; }
public ConcurrencyException(string entityName, object entityId,
int expectedVersion, int actualVersion)
: base($"并发冲突:{entityName}({entityId}) 期望版本{expectedVersion},实际版本{actualVersion}")
{
EntityName = entityName;
EntityId = entityId;
ExpectedVersion = expectedVersion;
ActualVersion = actualVersion;
}
}
4.4 权限异常 #
csharp
public class PermissionDeniedException : Exception
{
public string UserId { get; }
public string RequiredPermission { get; }
public string Resource { get; }
public PermissionDeniedException(string userId, string permission, string resource = null)
: base($"权限不足:用户{userId}没有{permission}权限")
{
UserId = userId;
RequiredPermission = permission;
Resource = resource;
}
}
五、异常层次结构设计 #
5.1 基类设计 #
csharp
public abstract class AppException : Exception
{
public string Code { get; }
public Severity Severity { get; }
protected AppException(string code, string message, Severity severity = Severity.Error)
: base(message)
{
Code = code;
Severity = severity;
}
protected AppException(string code, string message, Exception innerException,
Severity severity = Severity.Error)
: base(message, innerException)
{
Code = code;
Severity = severity;
}
}
public enum Severity { Info, Warning, Error, Critical }
5.2 派生异常 #
csharp
public class NotFoundException : AppException
{
public string EntityType { get; }
public object Id { get; }
public NotFoundException(string entityType, object id)
: base("NOT_FOUND", $"{entityType}未找到:{id}", Severity.Warning)
{
EntityType = entityType;
Id = id;
}
}
public class ConflictException : AppException
{
public ConflictException(string message)
: base("CONFLICT", message, Severity.Warning)
{
}
}
六、最佳实践 #
6.1 命名规范 #
csharp
public class UserNotFoundException : Exception { }
public class InvalidCredentialsException : Exception { }
public class InsufficientFundsException : Exception { }
6.2 提供有意义的消息 #
csharp
throw new UserNotFoundException($"用户ID {userId} 不存在");
6.3 包含内部异常 #
csharp
try
{
await SaveToDatabaseAsync(user);
}
catch (SqlException ex)
{
throw new UserSaveException("保存用户失败", ex);
}
6.4 使用异常层次 #
csharp
try
{
ProcessOrder(order);
}
catch (OrderValidationException ex)
{
}
catch (OrderProcessingException ex)
{
}
catch (OrderException ex)
{
}
七、总结 #
自定义异常要点:
| 要点 | 说明 |
|---|---|
| 继承Exception | 创建自定义异常 |
| 构造函数 | 提供标准构造函数 |
| 额外属性 | 添加错误详情 |
| 序列化 | 支持跨域传输 |
| 命名规范 | 以Exception结尾 |
下一步,让我们学习异常最佳实践!
最后更新:2026-03-26