Dart 异常处理全面指南

50 阅读7分钟

Dart 异常处理全面指南

目录

  1. 异常处理基础
  2. 自定义异常类
  3. 异步异常处理
  4. 高级设计模式
  5. 最佳实践
  6. 性能优化
  7. 测试策略
  8. 调试技巧

1. 异常处理基础

1.1 基本语法

try-catch 语句
try {
  var result = 10 ~/ 0; // 可能抛出异常的代码
  print('结果: $result');
} catch (e) {
  print('捕获异常: $e');
  print('异常类型: ${e.runtimeType}');
}
捕获特定异常类型
try {
  // 危险操作
  validateInput(data);
} on ArgumentError catch (e) {
  print('参数错误: ${e.message}');
} on FormatException catch (e) {
  print('格式错误: ${e.message}');
} catch (e) {
  print('其他异常: $e');
}
finally 块
try {
  performOperation();
} catch (e) {
  handleError(e);
} finally {
  // 无论是否发生异常都会执行
  cleanup();
}

1.2 抛出异常

抛出内置异常
void validateAge(int age) {
  if (age < 0) {
    throw ArgumentError.value(age, 'age', '年龄不能为负数');
  }
  if (age > 150) {
    throw RangeError.value(age, 0, 150, 'age', '年龄必须在0-150之间');
  }
}
重新抛出异常
void middleLayerOperation() {
  try {
    lowLevelOperation();
  } catch (e) {
    print('记录错误日志: $e');
    rethrow; // 重新抛出原始异常
  }
}

1.3 异常类型体系

Dart 内置异常类型
  • Exception: 基础异常接口
  • Error: 程序错误基类
  • ArgumentError: 参数错误
  • RangeError: 范围错误
  • StateError: 状态错误
  • FormatException: 格式异常
  • TimeoutException: 超时异常
  • UnsupportedError: 不支持的操作

2. 自定义异常类

2.1 异常类层次结构

abstract class AppException implements Exception {
  final String message;
  final String code;
  final DateTime timestamp;
  final dynamic originalError;
  
  const AppException(
    this.message, 
    this.code, {
    this.originalError,
    DateTime? timestamp,
  }) : timestamp = timestamp ?? DateTime.now();
  
  @override
  String toString() => '$runtimeType($code): $message';
  
  Map<String, dynamic> toJson() => {
    'type': runtimeType.toString(),
    'code': code,
    'message': message,
    'timestamp': timestamp.toIso8601String(),
    'originalError': originalError?.toString(),
  };
}

2.2 具体异常类型

业务逻辑异常
class BusinessException extends AppException {
  final String? field;
  final dynamic invalidValue;
  
  const BusinessException(
    String message,
    String code, {
    this.field,
    this.invalidValue,
    dynamic originalError,
  }) : super(message, code, originalError: originalError);
}
验证异常
class ValidationException extends BusinessException {
  final List<String> errors;
  
  const ValidationException(
    String message,
    this.errors, {
    String? field,
    dynamic invalidValue,
  }) : super(
    message, 
    'VALIDATION_ERROR',
    field: field,
    invalidValue: invalidValue,
  );
}
网络异常
class NetworkException extends AppException {
  final int? statusCode;
  final String? url;
  final Duration? timeout;
  
  const NetworkException(
    String message,
    String code, {
    this.statusCode,
    this.url,
    this.timeout,
    dynamic originalError,
  }) : super(message, code, originalError: originalError);
  
  factory NetworkException.timeout(String url, Duration timeout) {
    return NetworkException(
      '网络请求超时',
      'NETWORK_TIMEOUT',
      url: url,
      timeout: timeout,
    );
  }
}

2.3 异常处理策略

异常处理器接口
abstract class ExceptionHandler<T extends Exception> {
  bool canHandle(Exception exception);
  void handle(T exception);
}
异常管理器
class ExceptionManager {
  final List<ExceptionHandler> _handlers = [];
  
  void registerHandler(ExceptionHandler handler) {
    _handlers.add(handler);
  }
  
  void handleException(Exception exception) {
    for (var handler in _handlers) {
      if (handler.canHandle(exception)) {
        handler.handle(exception);
        return;
      }
    }
    _handleUnknownException(exception);
  }
}

3. 异步异常处理

3.1 Future 异常处理

基本异步异常处理
// 使用 async/await
try {
  var result = await performAsyncOperation();
  print('成功: $result');
} catch (e) {
  print('异步异常: $e');
}

// 使用 then/catchError
performAsyncOperation()
  .then((value) => print('成功: $value'))
  .catchError((error) => print('错误: $error'));
超时处理
try {
  var result = await longRunningOperation()
      .timeout(Duration(seconds: 30));
  print('在超时前完成: $result');
} on TimeoutException catch (e) {
  print('操作超时: ${e.message}');
}

// 带回退值的超时处理
var result = await longRunningOperation()
    .timeout(
      Duration(seconds: 10),
      onTimeout: () => 'default_value',
    );
并发异常处理
// 使用 Future.wait
try {
  var results = await Future.wait([
    task1(),
    task2(),
    task3(),
  ]);
  print('所有任务完成: $results');
} catch (e) {
  print('第一个异常: $e');
}

// 处理部分失败
var futures = [task1(), task2(), task3()];
var results = <String>[];
var errors = <String>[];

await Future.wait(futures.map((future) async {
  try {
    var result = await future;
    results.add(result);
  } catch (e) {
    errors.add(e.toString());
  }
}));

3.2 Stream 异常处理

基本 Stream 异常处理
stream.listen(
  (data) => print('数据: $data'),
  onError: (error) => print('错误: $error'),
  onDone: () => print('完成'),
  cancelOnError: false, // 错误时不取消订阅
);
await for 异常处理
try {
  await for (var data in stream) {
    print('处理数据: $data');
  }
} catch (e) {
  print('Stream 异常: $e');
}
Stream 转换异常处理
stream
  .map((data) {
    if (data == null) throw ArgumentError('数据不能为空');
    return processData(data);
  })
  .handleError((error) {
    print('转换错误: $error');
    return 'error_value'; // 提供默认值
  })
  .listen((result) => print('结果: $result'));

3.3 Isolate 异常处理

基本 Isolate 异常处理
try {
  var result = await Isolate.run(() {
    return performHeavyComputation();
  });
  print('计算结果: $result');
} catch (e) {
  print('Isolate 异常: $e');
}
复杂 Isolate 通信
var receivePort = ReceivePort();
var errorPort = ReceivePort();

var isolate = await Isolate.spawn(
  isolateEntryPoint,
  receivePort.sendPort,
  onError: errorPort.sendPort,
);

receivePort.listen((message) {
  print('收到消息: $message');
});

errorPort.listen((errorData) {
  print('收到错误: $errorData');
});

4. 高级设计模式

4.1 分层异常处理

表示层(Controller)
class UserController {
  void handleRequest(String action, Map<String, dynamic> data) {
    try {
      // 业务逻辑调用
      _processRequest(action, data);
    } on ValidationException catch (e) {
      _sendErrorResponse('VALIDATION_ERROR', e.message, e.errors);
    } on BusinessException catch (e) {
      _sendErrorResponse(e.code, e.message);
    } on SystemException catch (e) {
      _sendErrorResponse('SYSTEM_ERROR', '系统暂时不可用');
      _logSystemError(e);
    } catch (e) {
      _sendErrorResponse('UNKNOWN_ERROR', '未知错误');
      _logUnknownError(e);
    }
  }
}
业务逻辑层(Service)
class UserService {
  Map<String, dynamic> createUser(Map<String, dynamic> userData) {
    try {
      _validateUserData(userData);
      return _repository.create(userData);
    } on ValidationException {
      rethrow; // 向上传播验证异常
    } on DataAccessException catch (e) {
      throw SystemException('用户创建失败', 'USER_CREATE_FAILED', originalError: e);
    }
  }
}

4.2 异常处理模式

结果类型模式
class Result<T, E> {
  final T? _value;
  final E? _error;
  final bool _isSuccess;
  
  factory Result.success(T value) => Result._(value, null, true);
  factory Result.failure(E error) => Result._(null, error, false);
  
  bool get isSuccess => _isSuccess;
  T get value => _isSuccess ? _value! : throw StateError('结果为失败状态');
  E get error => !_isSuccess ? _error! : throw StateError('结果为成功状态');
}

Future<Result<String, String>> performOperation() async {
  try {
    var result = await dangerousOperation();
    return Result.success(result);
  } catch (e) {
    return Result.failure(e.toString());
  }
}
重试模式
Future<T> retryWithBackoff<T>(
  Future<T> Function() operation, {
  required int maxRetries,
  required Duration baseDelay,
}) async {
  var currentRetry = 0;
  
  while (currentRetry <= maxRetries) {
    try {
      return await operation();
    } catch (e) {
      currentRetry++;
      if (currentRetry > maxRetries) rethrow;
      
      var delay = Duration(
        milliseconds: baseDelay.inMilliseconds * (1 << (currentRetry - 1)),
      );
      await Future.delayed(delay);
    }
  }
  
  throw StateError('重试逻辑错误');
}
断路器模式
class CircuitBreaker {
  final int _failureThreshold;
  final Duration _recoveryTimeout;
  
  int _failureCount = 0;
  DateTime? _lastFailureTime;
  bool _isOpen = false;
  
  Future<T> execute<T>(Future<T> Function() operation) async {
    if (_isOpen) {
      if (_shouldAttemptReset()) {
        _reset();
      } else {
        throw Exception('断路器开启状态');
      }
    }
    
    try {
      var result = await operation();
      _onSuccess();
      return result;
    } catch (e) {
      _onFailure();
      rethrow;
    }
  }
}

4.3 异常监控和日志

异常监控器
class ExceptionMonitor {
  final Map<String, int> _exceptionCounts = {};
  final List<ExceptionRecord> _recentExceptions = [];
  
  void recordException(Exception exception, String context, [StackTrace? stackTrace]) {
    var exceptionType = exception.runtimeType.toString();
    _exceptionCounts[exceptionType] = (_exceptionCounts[exceptionType] ?? 0) + 1;
    
    var record = ExceptionRecord(
      exception: exception,
      context: context,
      timestamp: DateTime.now(),
      stackTrace: stackTrace,
    );
    
    _recentExceptions.add(record);
    _logException(record);
    _checkForPatterns(record);
  }
}
结构化日志记录
void _logException(ExceptionRecord record) {
  var logEntry = {
    'level': _getLogLevel(record.exception),
    'timestamp': record.timestamp.toIso8601String(),
    'exception_type': record.exception.runtimeType.toString(),
    'message': record.exception.toString(),
    'context': record.context,
    'stack_trace': record.stackTrace?.toString(),
  };
  
  print('异常日志: ${jsonEncode(logEntry)}');
}

5. 最佳实践

5.1 设计原则

快速失败(Fail Fast)
void processUser(String? name, int? age) {
  // 在方法开始时立即验证参数
  if (name == null || name.trim().isEmpty) {
    throw ArgumentError.notNull('name');
  }
  if (age == null || age < 0 || age > 150) {
    throw ArgumentError.value(age, 'age', '年龄必须在0-150之间');
  }
  
  // 后续处理逻辑...
}
异常安全保证
class BankAccount {
  void transferTo(BankAccount target, double amount) {
    if (amount > _balance) {
      throw StateError('余额不足');
    }
    
    // 保存原始状态
    var originalBalance = _balance;
    var originalTargetBalance = target._balance;
    
    try {
      // 执行转账
      _balance -= amount;
      target._balance += amount;
      
      // 可能失败的操作
      _notifyTransfer(amount);
    } catch (e) {
      // 回滚状态
      _balance = originalBalance;
      target._balance = originalTargetBalance;
      rethrow;
    }
  }
}
资源管理
T using<T>(Disposable resource, T Function(Disposable) action) {
  try {
    return action(resource);
  } finally {
    resource.dispose();
  }
}

// 使用示例
var result = using(DatabaseConnection(), (conn) {
  conn.connect();
  return conn.query('SELECT * FROM users');
});

5.2 异常处理指导原则

  1. 明确异常类型: 使用具体的异常类型而不是通用的 Exception
  2. 提供有用信息: 异常消息应该清楚地描述问题和可能的解决方案
  3. 避免空异常: 不要捕获异常后什么都不做
  4. 适当的异常级别: 在正确的抽象层次处理异常
  5. 文档化异常: 在 API 文档中说明可能抛出的异常

5.3 错误码 vs 异常

使用异常的场景
  • 异常情况(预期之外的错误)
  • 无法在本地处理的错误
  • 需要中断正常执行流程的错误
使用错误码的场景
  • 预期内的失败情况
  • 性能敏感的代码
  • 需要详细错误信息的场景
// 异常方式
int divide(int a, int b) {
  if (b == 0) throw ArgumentError('除数不能为零');
  return a ~/ b;
}

// 错误码方式
class DivisionResult {
  final int? value;
  final String? error;
  
  const DivisionResult.success(this.value) : error = null;
  const DivisionResult.error(this.error) : value = null;
  
  bool get isSuccess => error == null;
}

DivisionResult safeDivide(int a, int b) {
  if (b == 0) return DivisionResult.error('除数不能为零');
  return DivisionResult.success(a ~/ b);
}

6. 性能优化

6.1 异常处理性能考虑

避免使用异常进行控制流
// 错误方式
try {
  for (int i = 0; ; i++) {
    print(list[i]); // 依赖异常结束循环
  }
} catch (e) {
  // 处理结束
}

// 正确方式
for (int i = 0; i < list.length; i++) {
  print(list[i]);
}
异常对象池化
class ExceptionPool {
  final Queue<PooledException> _available = Queue<PooledException>();
  
  PooledException getException(String message) {
    if (_available.isNotEmpty) {
      var exception = _available.removeFirst();
      exception.updateMessage(message);
      return exception;
    }
    return PooledException(message);
  }
  
  void returnException(PooledException exception) {
    _available.add(exception);
  }
}

6.2 预分配和缓存

常用异常的预分配
class CommonExceptions {
  static final argumentNull = ArgumentError.notNull('argument');
  static final invalidState = StateError('invalid state');
  static final timeout = TimeoutException('operation timeout');
  
  // 根据需要创建新实例
  static ArgumentError argumentValue(dynamic value, String name) {
    return ArgumentError.value(value, name);
  }
}

7. 测试策略

7.1 异常测试框架

基本测试方法
void expectException<T extends Exception>(
  void Function() action, 
  String testName
) {
  try {
    action();
    print('✗ $testName: 失败 - 期望异常但未抛出');
  } on T {
    print('✓ $testName: 通过');
  } catch (e) {
    print('✗ $testName: 失败 - 异常类型错误: ${e.runtimeType}');
  }
}

void expectExceptionWithMessage(
  void Function() action,
  String expectedMessage,
  String testName,
) {
  try {
    action();
    print('✗ $testName: 失败 - 期望异常但未抛出');
  } catch (e) {
    if (e.toString().contains(expectedMessage)) {
      print('✓ $testName: 通过');
    } else {
      print('✗ $testName: 失败 - 异常消息不匹配');
    }
  }
}
异步异常测试
Future<void> testAsyncException() async {
  try {
    await Future.delayed(Duration(seconds: 2))
        .timeout(Duration(milliseconds: 500));
    print('✗ 超时测试失败 - 期望超时异常');
  } on TimeoutException {
    print('✓ 超时测试通过');
  } catch (e) {
    print('✗ 超时测试失败 - 异常类型错误: ${e.runtimeType}');
  }
}

7.2 Mock 和测试双重

异常注入
class MockRepository implements Repository {
  bool shouldThrowException = false;
  Exception? exceptionToThrow;
  
  @override
  Future<User> findById(String id) async {
    if (shouldThrowException && exceptionToThrow != null) {
      throw exceptionToThrow!;
    }
    return User(id: id, name: 'Test User');
  }
}

// 测试使用
void testExceptionHandling() {
  var mockRepo = MockRepository();
  var service = UserService(mockRepo);
  
  // 测试正常情况
  mockRepo.shouldThrowException = false;
  expectNoException(() async {
    await service.getUser('123');
  }, '正常获取用户');
  
  // 测试异常情况
  mockRepo.shouldThrowException = true;
  mockRepo.exceptionToThrow = DataAccessException('连接失败', 'DB_ERROR');
  
  expectException<SystemException>(() async {
    await service.getUser('123');
  }, '数据库异常处理');
}

8. 调试技巧

8.1 异常调试工具

调试断点
void debugBreakpoint(String message, {bool condition = true}) {
  if (condition && kDebugMode) {
    print('调试断点: $message');
    // 在开发环境中可以使用 debugger(); 语句
  }
}

// 使用示例
try {
  performComplexOperation();
} catch (e, stackTrace) {
  debugBreakpoint('复杂操作失败: $e');
  
  // 详细的调试信息
  _printExceptionDetails(e, stackTrace);
}
异常详情打印
void _printExceptionDetails(dynamic exception, StackTrace stackTrace) {
  print('异常详情:');
  print('  类型: ${exception.runtimeType}');
  print('  消息: $exception');
  print('  时间: ${DateTime.now().toIso8601String()}');
  
  if (exception is AppException) {
    print('  代码: ${exception.code}');
    print('  原始错误: ${exception.originalError}');
  }
  
  print('  调用栈:');
  var stackLines = stackTrace.toString().split('\n');
  for (int i = 0; i < min(10, stackLines.length); i++) {
    print('    ${stackLines[i]}');
  }
}

8.2 性能分析

异常性能统计
class ExceptionProfiler {
  final Map<String, ExceptionStats> _stats = {};
  
  void recordException(Exception exception, Duration handlingTime) {
    var type = exception.runtimeType.toString();
    var stats = _stats.putIfAbsent(type, () => ExceptionStats());
    
    stats.count++;
    stats.totalTime += handlingTime;
    stats.lastOccurrence = DateTime.now();
  }
  
  void printStats() {
    print('\n异常处理性能统计:');
    _stats.forEach((type, stats) {
      print('$type:');
      print('  次数: ${stats.count}');
      print('  总时间: ${stats.totalTime.inMicroseconds}μs');
      print('  平均时间: ${stats.totalTime.inMicroseconds ~/ stats.count}μs');
      print('  最后发生: ${stats.lastOccurrence}');
    });
  }
}

class ExceptionStats {
  int count = 0;
  Duration totalTime = Duration.zero;
  DateTime? lastOccurrence;
}

8.3 日志集成

结构化异常日志
class ExceptionLogger {
  static void logException(
    Exception exception, 
    String context, 
    StackTrace? stackTrace,
    {Map<String, dynamic>? additionalData}
  ) {
    var logEntry = {
      'timestamp': DateTime.now().toIso8601String(),
      'level': _getLogLevel(exception),
      'exception_type': exception.runtimeType.toString(),
      'message': exception.toString(),
      'context': context,
      'stack_trace': stackTrace?.toString(),
      ...?additionalData,
    };
    
    // 发送到日志系统
    _sendToLogSystem(logEntry);
  }
  
  static String _getLogLevel(Exception exception) {
    if (exception is ValidationException) return 'WARNING';
    if (exception is BusinessException) return 'INFO';
    if (exception is SystemException) return 'ERROR';
    return 'CRITICAL';
  }
  
  static void _sendToLogSystem(Map<String, dynamic> logEntry) {
    // 实际实现中这里会发送到外部日志系统
    print('日志: ${jsonEncode(logEntry)}');
  }
}

9. 实际应用场景

9.1 Web API 异常处理

class ApiExceptionHandler {
  static Map<String, dynamic> handleException(Exception exception) {
    if (exception is ValidationException) {
      return {
        'status': 400,
        'error': {
          'code': 'VALIDATION_ERROR',
          'message': exception.message,
          'details': exception.errors,
        }
      };
    }
    
    if (exception is BusinessException) {
      return {
        'status': 422,
        'error': {
          'code': exception.code,
          'message': exception.message,
        }
      };
    }
    
    if (exception is SecurityException) {
      return {
        'status': exception.code == 'UNAUTHORIZED' ? 401 : 403,
        'error': {
          'code': exception.code,
          'message': exception.message,
        }
      };
    }
    
    // 系统异常不暴露详细信息
    return {
      'status': 500,
      'error': {
        'code': 'INTERNAL_ERROR',
        'message': '服务器内部错误',
      }
    };
  }
}

9.2 数据库操作异常处理

class DatabaseService {
  Future<User> createUser(Map<String, dynamic> userData) async {
    try {
      var user = await _database.insert('users', userData);
      return User.fromMap(user);
    } on PostgreSQLException catch (e) {
      if (e.code == '23505') { // 唯一约束违反
        throw BusinessException('用户已存在', 'USER_EXISTS');
      }
      throw DataAccessException('数据库操作失败', 'DB_ERROR', originalError: e);
    } on TimeoutException {
      throw DataAccessException('数据库操作超时', 'DB_TIMEOUT');
    } catch (e) {
      throw SystemException('用户创建失败', 'USER_CREATE_FAILED', originalError: e);
    }
  }
}

9.3 文件操作异常处理

class FileService {
  Future<String> readFile(String path) async {
    try {
      var file = File(path);
      if (!await file.exists()) {
        throw BusinessException('文件不存在', 'FILE_NOT_FOUND');
      }
      
      return await file.readAsString();
    } on PathAccessException catch (e) {
      throw BusinessException('无权限访问文件', 'ACCESS_DENIED', originalError: e);
    } on FileSystemException catch (e) {
      if (e.osError?.errorCode == 2) { // 文件不存在
        throw BusinessException('文件不存在', 'FILE_NOT_FOUND', originalError: e);
      }
      throw SystemException('文件系统错误', 'FILE_SYSTEM_ERROR', originalError: e);
    } catch (e) {
      throw SystemException('文件读取失败', 'FILE_READ_FAILED', originalError: e);
    }
  }
}

10. 总结

10.1 关键要点

  1. 异常应该是异常的: 只在真正异常的情况下使用异常
  2. 快速失败: 尽早发现和报告错误
  3. 异常安全: 确保异常发生时程序状态的一致性
  4. 适当的抽象层次: 在正确的层次处理和转换异常
  5. 完整的错误信息: 提供足够的信息来诊断和解决问题
  6. 资源管理: 确保异常发生时正确清理资源
  7. 监控和日志: 建立完善的异常监控和日志系统

10.2 避免的反模式

  1. 空的 catch 块: 捕获异常后什么都不做
  2. 过度捕获: 捕获过于宽泛的异常类型
  3. 异常控制流: 使用异常进行正常的程序控制
  4. 异常链断裂: 丢失原始异常信息
  5. 资源泄漏: 异常发生时未正确清理资源
  6. 性能问题: 在性能敏感的代码中频繁使用异常

10.3 进阶学习建议

  1. 研究 Dart 官方异常处理最佳实践
  2. 学习其他语言的异常处理模式
  3. 实践企业级应用的异常处理架构
  4. 掌握异常处理的性能优化技巧
  5. 建立完善的异常监控和告警系统

通过遵循这些原则和最佳实践,您可以构建出健壮、可维护的 Dart 应用程序,能够优雅地处理各种异常情况。