前言
今天想系统记录学习一下C#的异常,以后写代码抛出异常更加规范,然后问AI列举了常用的错误类型,而且AI还说在一些性能要求高的地方尽量不要抛出异常,因为异常消耗的系统资源较多,给出了一个比较好的方案,我记录一下.
常见异常类型及适用场景
异常类型 | 命名空间 | 适用场景 |
---|---|---|
System.Exception | System | 所有异常的基类,通常不直接抛出 |
System.SystemException | System | 系统级异常的基类 |
System.ArgumentException | System | 向方法传递了无效参数 |
System.ArgumentNullException | System | 参数为 null,而方法不允许 null |
System.ArgumentOutOfRangeException | System | 参数超出了允许的范围 |
System.NullReferenceException | System | 访问了 null 对象的成员 |
System.IndexOutOfRangeException | System | 数组或集合索引越界 |
System.InvalidOperationException | System | 对象当前状态不支持该操作 |
System.IO.IOException | System.IO | 文件或流操作出错 |
System.DivideByZeroException | System | 除数为 0 时抛出 |
System.FormatException | System | 字符串格式不正确,比如无法转换为数字 |
System.OverflowException | System | 算术运算溢出(如超出 int 范围) |
System.NotImplementedException | System | 某个方法尚未实现(通常用于占位) |
System.NotSupportedException | System | 某方法在当前环境不受支持 |
System.TimeoutException | System | 操作超时(例如网络、串口、线程) |
System.UnauthorizedAccessException | System | 无权限访问资源(如文件、注册表) |
自定义异常类型
当系统提供的异常类型不够用时,可以自定义异常:
public class MyCustomException : Exception
{
public MyCustomException(string message) : base(message) { }
}
用法:
throw new MyCustomException("出现自定义异常");
异常处理最佳实践
- 尽量捕获具体异常类型,不要用 catch(Exception) 泛滥使用。
- 不要吞掉异常信息,应该至少记录日志或告知用户。
- 使用 finally 块进行资源释放(如关闭文件、数据库连接)。
- 性能敏感代码中避免频繁使用异常。异常机制是昂贵的,不应用于流程控制。
- 只在必要时抛出异常,例如不可恢复的错误,而不是用于逻辑判断。
带有错误返回的方法
public readonly struct Result
{
public bool Success { get; }
private readonly string? _message;
public string? Message => Success ? null : _message;
private Result(bool success, string? message)
{
Success = success;
_message = message;
}
public static Result Ok() => new Result(true, null);
public static Result Ok(string message) => new Result(true, message);
public static Result Fail(string message) => new Result(false, message);
}
public readonly struct Result<T>
{
public bool Success { get; }
private readonly string? _message;
public string? Message => Success ? null : _message;
public T Data { get; }
private Result(bool success, T data, string? message)
{
Success = success;
Data = data;
_message = message;
}
public static Result<T> Ok(T data) => new Result<T>(true, data, null);
public static Result<T> Ok(T data, string message) => new Result<T>(true, data, message);
public static Result<T> Fail(string message) => new Result<T>(false, default!, message);
}
使用方法
// 1. 无泛型版本
Result r1 = Result.Ok();
Console.WriteLine(r1.Success); // True
Console.WriteLine(r1.Message ?? "无消息"); // 无消息
Result r2 = Result.Fail("操作失败");
Console.WriteLine(r2.Success); // False
Console.WriteLine(r2.Message); // 操作失败
// 2. 泛型版本成功返回数据
Result<int> r3 = Result<int>.Ok(123);
if (r3.Success)
{
Console.WriteLine($"成功,数据是 {r3.Data}"); // 成功,数据是 123
}
// 3. 泛型版本失败
Result<string> r4 = Result<string>.Fail("找不到数据");
Console.WriteLine(r4.Success); // False
Console.WriteLine(r4.Message); // 找不到数据
// 4. 泛型版本成功带消息
Result<string> r5 = Result<string>.Ok("hello world", "附加消息");
Console.WriteLine(r5.Success); // True
Console.WriteLine(r5.Message); // 附加消息
Console.WriteLine(r5.Data); // hello world
我的建议
对于一些用户的输入等可以提示的消息使用 Result ,对于捕获到的错误,在提示用户的同时,也写入日志之中,这样更有助于规范的代码.
本文由 jxxxy 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。