自定义异常 #

一、为什么需要自定义异常 #

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