异常处理
异常处理是编写健壮程序的关键。本章将详细介绍 Sii 2 中的错误处理机制、异常类型和最佳实践。
错误处理概述
在 Sii 2 中,错误处理主要通过以下方式实现:
- 返回值检查:函数返回
void或特定类型表示成功/失败 - 类型检查:使用
typeins检查返回值类型 - 条件判断:使用
if语句检查操作是否成功 - 错误码系统:运行时服务器提供标准化的错误码
错误码系统
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 操作,了解文件读写和标准输入输出。