跳到主要内容

异常处理

异常处理是编写健壮程序的关键。本章将详细介绍 Sii 2 中的错误处理机制、异常类型和最佳实践。

错误处理概述

在 Sii 2 中,错误处理主要通过以下方式实现:

  1. 返回值检查:函数返回 void 或特定类型表示成功/失败
  2. 类型检查:使用 typeins 检查返回值类型
  3. 条件判断:使用 if 语句检查操作是否成功
  4. 错误码系统:运行时服务器提供标准化的错误码

错误码系统

Sii 2 运行时服务器定义了标准化的错误码,用于标识不同类型的错误:

数据库错误 (1000-1999)

// 1000: DB_CONNECTION_FAILED - 数据库连接失败
// 1001: DB_QUERY_FAILED - 数据库查询失败
// 1002: DB_INVALID_QUERY - 无效的数据库查询

文件系统错误 (2000-2999)

// 2000: FS_FILE_NOT_FOUND - 文件未找到
// 2001: FS_PERMISSION_DENIED - 权限被拒绝
// 2002: FS_IO_ERROR - IO 错误

执行错误 (3000-3999)

// 3000: EXEC_COMMAND_FAILED - 命令执行失败
// 3001: EXEC_TIMEOUT - 命令执行超时

输入输出错误 (4000-4999)

// 4000: IO_INVALID_INPUT - 无效输入
// 4001: IO_PARSE_ERROR - 解析错误

HTTP 错误 (5000-5999)

// 5000: HTTP_ROUTE_NOT_FOUND - 路由未找到
// 5001: HTTP_INVALID_REQUEST - 无效请求

数据结构错误 (6000-6999)

// 6000: DATA_STRUCTURE_NOT_FOUND - 数据结构未找到
// 6001: DATA_INVALID_OPERATION - 无效操作

运行时错误 (7000-7999)

// 7000: RUNTIME_ERROR - 运行时错误
// 7001: RUNTIME_TIMEOUT - 操作超时

函数返回值检查

可选返回值

函数可以返回 void 表示失败,或返回具体值表示成功:

func divide(a: int, b: int): int | void {
if (b == 0) {
sii.io.print("Error: Division by zero");
back; // 返回 void 表示失败
}
back a / b; // 返回结果表示成功
}

// 使用
let result: int | void = divide(10, 2);
if (typeins(result) == "int") {
let value: int = result as int;
sii.io.print("Result: " + value.toString());
} else {
sii.io.print("Division failed");
}

布尔返回值

函数可以返回 bool 表示操作是否成功:

func saveToFile(path: string, content: string): bool {
// 尝试写入文件
// 如果成功返回 true,失败返回 false
if (sii.exists(path)) {
sii.writeText(path, content);
back true;
} else {
back false;
}
}

// 使用
let success: bool = saveToFile("./data.txt", "Hello");
if (success) {
sii.io.print("File saved successfully");
} else {
sii.io.print("Failed to save file");
}

文件操作错误处理

检查文件是否存在

func readFileSafely(path: string): string | void {
if (!sii.exists(path)) {
sii.io.print("Error: File not found: " + path);
back;
}
back sii.readText(path);
}

// 使用
let content: string | void = readFileSafely("./config.json");
if (typeins(content) == "string") {
let text: string = content as string;
sii.io.print("File content: " + text);
} else {
sii.io.print("Failed to read file");
}

安全的文件写入

func writeFileSafely(path: string, content: string): bool {
// 检查父目录是否存在
let parentDir: string = sii.pathJoin(path, "..");
if (!sii.exists(parentDir)) {
sii.mkdirs(parentDir);
}

// 尝试写入
sii.writeText(path, content);
back true;
}

输入验证

类型检查

func processUserInput(input: unknown): void {
let inputType: string = typeins(input);

if (inputType == "int") {
let num: int = input as int;
if (num > 0 && num < 100) {
sii.io.print("Valid number: " + num.toString());
} else {
sii.io.print("Error: Number out of range");
}
} else if (inputType == "string") {
let text: string = input as string;
if (text.length() > 0) {
sii.io.print("Valid string: " + text);
} else {
sii.io.print("Error: Empty string");
}
} else {
sii.io.print("Error: Invalid input type");
}
}

输入转换

func safeStringToInt(s: string): int | void {
// 尝试转换为整数
let num: int = s.toInt();
// 注意:需要检查转换是否成功
// 如果 toInt() 失败,可能需要其他方式检查
back num;
}

数组操作错误处理

安全的数组访问

func safeArrayAccess(numbers: arr<int>, index: int): int | void {
if (index < 0 || index >= numbers.length()) {
sii.io.print("Error: Array index out of bounds");
back;
}
back numbers[index];
}

// 使用
let numberList: arr<int> = new Array();
numberList[0] = 10;
numberList[1] = 20;

let value: int | void = safeArrayAccess(numberList, 0);
if (typeins(value) == "int") {
sii.io.print("Value: " + (value as int).toString());
}

数组操作检查

func removeArrayElement(numbers: arr<int>, index: int): bool {
if (index < 0 || index >= numbers.length()) {
sii.io.print("Error: Invalid index");
back false;
}
rmv numbers[index]; // 使用 rmv 语句删除元素
back true;
}

网络操作错误处理

HTTP 请求错误处理

func safeHttpGet(url: string): string | void {
// 假设有 HTTP 客户端
// let response = sii.http.get(url);
// if (response.status == 200) {
// back response.body;
// } else {
// sii.io.print("HTTP Error: " + string(response.status));
// back;
// }
back; // 占位
}

错误处理模式

模式 1:早期返回

func processData(data: string): string | void {
// 早期检查,失败立即返回
if (data.length() == 0) {
sii.io.print("Error: Empty data");
back;
}

if (data.length() > 1000) {
sii.io.print("Error: Data too long");
back;
}

// 处理逻辑
let processed: string = "Processed: " + data;
back processed;
}

模式 2:错误累积

func validateUser(name: string, email: string, age: int): bool {
let isValid: bool = true;

if (name.length() == 0) {
sii.io.print("Error: Name is required");
isValid = false;
}

if (email.length() == 0) {
sii.io.print("Error: Email is required");
isValid = false;
}

if (age < 0 || age > 150) {
sii.io.print("Error: Invalid age");
isValid = false;
}

back isValid;
}

模式 3:错误包装

func performOperation(): int | void {
let step1Result: int | void = step1();
if (typeins(step1Result) != "int") {
sii.io.print("Error: Step 1 failed");
back;
}

let step2Result: int | void = step2(step1Result as int);
if (typeins(step2Result) != "int") {
sii.io.print("Error: Step 2 failed");
back;
}

back step2Result as int;
}

错误日志记录

使用标准库日志

func handleError(operation: string, error: string): void {
sii.error("Operation failed: " + operation);
sii.error("Error: " + error);
}

func logWarning(message: string): void {
sii.warn(message);
}

func logInfo(message: string): void {
sii.log(message);
}

防御性编程

空值检查

func processOptional(value: int | void): void {
if (typeins(value) == "int") {
let num: int = value as int;
sii.io.print("Processing: " + num.toString());
} else {
sii.io.print("Warning: Value is null");
}
}

边界检查

func safeIndexAccess(numbers: arr<int>, index: int): int | void {
// 检查数组是否为空
if (numbers.length() == 0) {
sii.io.print("Error: Array is empty");
back;
}

// 检查索引范围
if (index < 0) {
sii.io.print("Error: Negative index");
back;
}

if (index >= numbers.length()) {
sii.io.print("Error: Index out of bounds");
back;
}

back numbers[index];
}

类型验证

func validateType(value: unknown, expectedType: string): bool {
let actualType: string = typeins(value);
if (actualType != expectedType) {
sii.io.print("Error: Expected " + expectedType + ", got " + actualType);
back false;
}
back true;
}

错误处理最佳实践

1. 明确的错误信息

提供清晰、有用的错误信息:

// ✅ 好的错误信息
sii.io.print("Error: Cannot divide by zero. Dividend: " + a.toString() + ", Divisor: " + b.toString());

// ❌ 不好的错误信息
sii.io.print("Error");

2. 适当的错误级别

根据错误的严重程度选择合适的处理方式:

func criticalOperation(): void {
// 关键操作失败,记录错误并停止
sii.error("Critical operation failed");
back;
}

func warningOperation(): void {
// 警告级别,记录但继续执行
sii.warn("Warning: Operation may have issues");
}

3. 错误恢复

在可能的情况下提供错误恢复机制:

func readConfigWithFallback(): string {
let configPath: string = "./config.json";

if (sii.exists(configPath)) {
back sii.readText(configPath);
} else {
sii.warn("Config file not found, using defaults");
back "{}"; // 返回默认配置
}
}

4. 错误传播

在多层函数调用中正确传播错误:

func innerOperation(): int | void {
// 内部操作
back 42;
}

func middleOperation(): int | void {
let result: int | void = innerOperation();
if (typeins(result) != "int") {
back; // 传播错误
}
back result as int;
}

func outerOperation(): void {
let result: int | void = middleOperation();
if (typeins(result) != "int") {
sii.error("Operation chain failed");
back;
}
sii.io.print("Success: " + (result as int).toString());
}

完整示例

示例:文件处理工具

func processFile(inputPath: string, outputPath: string): bool {
// 1. 检查输入文件
if (!sii.exists(inputPath)) {
sii.error("Input file not found: " + inputPath);
back false;
}

// 2. 读取文件
let content: string = sii.readText(inputPath);
if (content.length() == 0) {
sii.warn("Input file is empty");
}

// 3. 处理内容
let processed: string = "Processed: " + content;

// 4. 确保输出目录存在
let outputDir: string = sii.pathJoin(outputPath, "..");
if (!sii.exists(outputDir)) {
sii.mkdirs(outputDir);
}

// 5. 写入输出文件
sii.writeText(outputPath, processed);

// 6. 验证写入
if (sii.exists(outputPath)) {
sii.log("File processed successfully");
back true;
} else {
sii.error("Failed to write output file");
back false;
}
}

小结

本章介绍了 Sii 2 中的异常处理机制:

  • 错误码系统:标准化的错误码分类
  • 返回值检查:使用可选返回值和类型检查
  • 文件操作错误处理:安全的文件读写
  • 输入验证:类型检查和转换
  • 错误处理模式:早期返回、错误累积、错误包装
  • 防御性编程:空值检查、边界检查、类型验证
  • 最佳实践:明确的错误信息、适当的错误级别、错误恢复

下一章我们将学习基础 I/O 操作,了解文件读写和标准输入输出。