📅 Day 17:异常处理与日志记录(Exception Handling & Logging)
✅ 学习目标:
- 理解什么是异常(Exception),它在程序运行时的作用;
- 掌握使用
try-catch-finally
进行基本的异常捕获和处理; - 理解不同类型的异常及其应用场景;
- 学会使用 C# 中的内置异常类;
- 理解日志记录的重要性;
- 能够使用
ILogger
或ILoggerProvider
实现基础日志记录; - 编写一个具有异常处理和日志记录功能的小型文件读写工具。
🧠 一、什么是异常(Exception)?
异常(Exception) 是程序在运行过程中发生的错误或意外行为。如果不加以处理,这些异常会导致程序崩溃。
C# 提供了结构化的异常处理机制,使我们可以在程序出错时优雅地进行恢复、提示用户或记录错误信息。
🛑 常见异常类型
异常类型 | 描述 |
---|---|
Exception | 所有异常的基类 |
NullReferenceException | 尝试访问 null 对象的成员 |
FileNotFoundException | 文件未找到 |
IOException | 输入/输出错误,如无法读取文件 |
UnauthorizedAccessException | 没有权限访问某个资源 |
FormatException | 字符串格式不正确,如转换失败 |
IndexOutOfRangeException | 数组索引超出范围 |
🔁 二、异常处理语法:try-catch-finally
try
{// 可能引发异常的代码
}
catch (Exception ex)
{// 处理异常Console.WriteLine("发生异常:" + ex.Message);
}
finally
{// 总是执行,用于释放资源等操作
}
✅ 示例 1:捕获特定异常
try
{int.Parse("abc"); // 会抛出 FormatException
}
catch (FormatException ex)
{Console.WriteLine("格式错误:" + ex.Message);
}
catch (Exception ex)
{Console.WriteLine("未知错误:" + ex.Message);
}
✅ 示例 2:多异常处理(.NET 6+ 支持 catch when
条件过滤)
try
{object obj = null;Console.WriteLine(obj.ToString());
}
catch (NullReferenceException ex) when (ex.Message.Contains("obj"))
{Console.WriteLine("空引用异常,但仅处理特定条件");
}
catch (NullReferenceException)
{Console.WriteLine("普通空引用异常");
}
🧱 三、自定义异常(Custom Exception)
你可以创建自己的异常类来表示业务逻辑中的特定错误。
public class InvalidAgeException : Exception
{public InvalidAgeException() : base("年龄无效,请输入大于0的整数。") { }
}class Program
{static void ValidateAge(int age){if (age <= 0)throw new InvalidAgeException();}static void Main(){try{ValidateAge(-5);}catch (InvalidAgeException ex){Console.WriteLine("自定义异常:" + ex.Message);}}
}
📝 四、什么是日志记录(Logging)?
日志记录(Logging) 是将程序运行过程中的信息保存到文本文件、数据库或其他存储介质中的一种方式。它对于调试、监控系统状态、分析问题原因非常重要。
🧩 五、C# 中的日志记录方法
方式 1:使用 Console.WriteLine()
(适合简单调试)
Console.WriteLine($"[INFO] 正在加载配置文件...");
方式 2:写入日志文件(推荐做法)
string logPath = "app.log";
string message = $"[{DateTime.Now}] [ERROR] 发生异常:{ex.Message}{Environment.NewLine}";File.AppendAllText(logPath, message);
方式 3:使用 Microsoft.Extensions.Logging(适用于 .NET Core / .NET 5+)
安装包:
dotnet add package Microsoft.Extensions.Logging
dotnet add package Microsoft.Extensions.Logging.Console
示例代码:
using Microsoft.Extensions.Logging;class Program
{static void Main(){using var loggerFactory = LoggerFactory.Create(builder =>{builder.AddSimpleConsole(options => options.IncludeScopes = true);});ILogger logger = loggerFactory.CreateLogger<Program>();logger.LogInformation("程序启动成功");logger.LogWarning("这是一个警告信息");logger.LogError("这是一个错误信息");}
}
💪 实战练习:带异常处理和日志记录的文件读写器
功能要求:
- 用户输入文件名并尝试读取内容;
- 如果文件不存在或读取失败,捕获异常;
- 所有操作记录日志到文件;
- 使用自定义异常处理非法输入。
示例代码:
using System;
using System.IO;class Program
{static string logPath = "file_reader_log.txt";static void Main(){while (true){Console.Write("请输入要读取的文件路径(输入 q 退出):");string path = Console.ReadLine();if (path.ToLower() == "q")break;try{string content = ReadFileContent(path);Console.WriteLine("文件内容如下:\n" + content);}catch (FileNotFoundException ex){LogError(ex.Message);Console.WriteLine("错误:" + ex.Message);}catch (IOException ex){LogError(ex.Message);Console.WriteLine("IO 错误:" + ex.Message);}catch (InvalidFilePathException ex){LogError(ex.Message);Console.WriteLine("路径错误:" + ex.Message);}catch (Exception ex){LogError(ex.Message);Console.WriteLine("未知错误:" + ex.Message);}}}static string ReadFileContent(string path){if (string.IsNullOrWhiteSpace(path))throw new InvalidFilePathException("文件路径不能为空");if (!File.Exists(path))throw new FileNotFoundException("文件不存在", path);return File.ReadAllText(path);}static void LogError(string message){string logEntry = $"[{DateTime.Now}] {message}{Environment.NewLine}";File.AppendAllText(logPath, logEntry);}
}public class InvalidFilePathException : Exception
{public InvalidFilePathException(string message) : base(message) { }
}
📝 小结
今天你学会了:
- 如何使用
try-catch-finally
捕获和处理异常; - 理解了常见异常类型及其用途;
- 掌握了如何编写自定义异常类以增强程序可维护性;
- 学会了多种方式实现日志记录,包括控制台、文件和
Microsoft.Extensions.Logging
; - 编写了一个完整的带异常处理和日志记录的文件读写工具。
这些技能将帮助你在开发健壮的应用程序、提升用户体验和排查生产环境问题方面大显身手!
🧩 下一步学习方向(Day 18)
明天我们将进入一个新的主题 —— 多线程编程(Multithreading),你将学会如何利用多线程提高程序性能,避免界面冻结,并发执行任务。