C#异常概念与try-catch入门

作者:烛阴日期:2025/12/5

一、什么是异常,我们为何需要它?

1. 编程世界里的“意外”

在C#中,异常是在程序执行期间发生的、中断了正常指令流的“反常”或“错误”事件。它不是我们通常所说的“BUG”(逻辑错误),比如你本想做加法却写了减法;也不是“语法错误”,那种在编译时就会被编译器指出的拼写错误。异常是运行时的错误,是程序在“活着”的时候遇到的突发状况。

常见的异常场景包括:

  • 尝试打开一个不存在的文件。
  • 网络连接突然中断。
  • 请求的内存过大,系统无法分配。
  • 数组索引超出了范围。
  • 尝试对一个 null 的对象进行操作。

2. 异常的代价

  • 程序突然终止:这是最直接的后果。对于用户来说,这意味着他们正在进行的工作(比如编辑文档、填写表单)可能会瞬间丢失,体验极差。
  • 数据损坏:例如一个转账操作,在扣除A账户金额后、增加B账户金额前,程序因为一个异常而崩溃。这将导致账目不平,数据状态不一致。
  • 暴露敏感信息:在Web应用中,一个未处理的异常可能会将包含数据库连接字符串、服务器内部路径等敏感信息的完整错误堆栈信息(Stack Trace)暴露给最终用户,构成严重的安全隐患。
  • 资源泄露:如果程序在打开文件或数据库连接后,在关闭它们之前崩溃,这些宝贵的系统资源将无法被释放,久而久之会耗尽系统资源,导致整个系统变慢甚至瘫痪。

二、try-catch块的基础语法与工作原理

try-catch 语句是C#中用于处理异常的基本工具。它的逻辑非常符合人类的直觉:“尝试做某件事,如果出了问题,就这样补救”。

1. try 块:划定“风险区”

try 关键字后面跟着一个代码块 {},我们将所有可能抛出异常的代码都放在这个代码块里。

1try
2{
3    // 这里是“风险区”
4    Console.WriteLine("请输入一个数字:");
5    int number = int.Parse(Console.ReadLine());
6    int result = 100 / number;
7    Console.WriteLine($"100除以{number}的结果是:{result}");
8} 
9catch(Exception e)
10{
11    Console.WriteLine(e.ToString()); 
12}
13
14

在上面的代码中,int.Parse() 可能会因为用户输入非数字字符而抛出 FormatException,而 100 / number 可能会因为用户输入0而抛出 DivideByZeroException

2. catch 块:部署“应急预案”

catch 关键字紧跟在 try 块之后,它也包含一个代码块。当 try 块中的任何一条语句抛出异常时,程序的正常执行流会立即中断,然后CLR(公共语言运行时)会寻找一个能够“接住”这个异常的 catch 块。

最基本的 catch 块:

1try
2{
3    // ... 风险代码 ...
4}
5catch
6{
7    // 异常发生时,执行这里的代码
8    Console.WriteLine("发生了一个未知错误!");
9}
10

这种不带任何参数的 catch 块可以捕获任何类型的异常,但它有一个巨大的缺点:你不知道具体发生了什么错误。这就像一个消防员赶到现场只知道“着火了”,却不知道是电线起火还是厨房起火,无法采取针对性的灭火措施。

3. 捕获具体的异常信息:catch (ExceptionType ex)

C#允许我们在 catch 后面指定要捕获的异常类型,并提供一个变量来接收这个异常对象。

System.Exception 是所有异常类型的基类。因此,catch (Exception ex) 可以捕获几乎所有类型的异常,并且通过变量 ex,我们可以访问到关于异常的宝贵信息。

1try
2{
3    Console.WriteLine("请输入一个数组索引(0-2):");
4    int[] numbers = { 10, 20, 30 };
5    int index = int.Parse(Console.ReadLine());
6    Console.WriteLine($"索引 {index} 上的值为: {numbers[index]}");
7}
8catch (Exception ex)
9{
10    Console.WriteLine("\n--- 程序出现问题!---");
11    Console.WriteLine($"错误类型: {ex.GetType().Name}"); // 获取异常的具体类型名
12    Console.WriteLine($"错误信息: {ex.Message}");        // 获取异常的描述信息
13    Console.WriteLine("--- 详细堆栈跟踪 ---");
14    Console.WriteLine(ex.StackTrace);                  // 获取异常发生时的调用堆栈
15    Console.WriteLine("----------------------");
16}
17
18Console.WriteLine("\n程序已通过异常处理,继续执行...");
19

三、玩转多catch块与异常层次结构

1. 多catch块进行细分异常

一个 try 块后面可以跟多个 catch 块,每个 catch 块负责处理一种特定类型的异常。CLR在匹配 catch 块时,会从上到下依次检查,并执行第一个能够匹配异常类型的 catch 块。

“匹配”的规则是:如果抛出的异常类型 catch 块中声明的类型,或者是其子类,则匹配成功。

这就引出了多catch块最重要的规则:catch 块的顺序必须是从最具体(子类)到最通用(父类)。

示例:一个健壮的文件读取操作

1public void ProcessFile(string filePath)
2{
3    try
4    {
5        string content = System.IO.File.ReadAllText(filePath);
6        Console.WriteLine("文件内容处理成功!");
7    }
8    catch (System.IO.FileNotFoundException ex) // 最具体的异常
9    {
10        Console.WriteLine($"错误:文件 '{filePath}' 不存在。请检查路径是否正确。");
11    }
12    catch (System.UnauthorizedAccessException ex) // 另一个具体的异常
13    {
14        Console.WriteLine($"错误:程序没有权限访问文件 '{filePath}'。");
15    }
16    catch (System.IO.IOException ex) // 捕获其他所有IO相关的异常
17    {
18        Console.WriteLine($"读取文件时发生 I/O 错误: {ex.Message}");
19    }
20    catch (Exception ex) // 最后的“万能捕手”,捕获所有其他意想不到的异常
21    {
22        Console.WriteLine($"发生未知错误,请联系技术支持。");
23        // 在真实应用中,这里应该记录完整的ex.ToString()到日志文件
24        // Log.Error(ex.ToString()); 
25    }
26}
27

分析:

  • 如果文件不存在,第一个 catch (FileNotFoundException) 会被执行。
  • 如果文件存在但程序没有读取权限,第二个 catch (UnauthorizedAccessException) 会被执行。
  • 如果发生其他I/O错误(如磁盘已满),由于这些错误类型(如DiskFullException)通常继承自 IOException,第三个 catch 块会被执行。
  • 如果发生了完全无关的错误(比如在后续处理中出现 OutOfMemoryException),最后的 catch (Exception) 会作为兜底防线被触发。

如果你把 catch (Exception ex) 放在最前面,那么它会捕获所有异常,后面的具体 catch 块将永远没有机会执行,编译器甚至会因此报错。

2. 警惕空catch块和“吞噬”异常

有时候,你可能会看到这样的代码:

1// 警告:极度危险的代码!
2try
3{
4    SomeRiskyOperation();
5}
6catch (Exception)
7{
8    // 什么也不做
9}
10

这被称为“吞噬异常”或“异常黑洞”。代码的作者可能认为“我知道这里可能出错,但我不关心”。这是一个极其危险的坏习惯!

为什么危险?

  1. 隐藏问题:一个严重的问题(比如数据库连接失败)发生了,但程序假装什么都没发生,继续往下执行。这很可能导致后续代码在错误的数据基础上运行,引发更隐蔽、更难以调试的错误,甚至导致数据永久性损坏。
  2. 调试噩梦:当程序出现奇怪的行为时,你将没有任何线索。没有日志,没有崩溃报告,错误就像人间蒸发了一样。

正确的做法是:即使你认为可以从某个异常中恢复,也至少应该记录它

1try
2{
3    // ...
4}
5catch (SomeExpectedAndRecoverableException ex)
6{
7    // 记录下来,以备后续分析
8    Log.Warning($"一个可恢复的错误发生了: {ex.Message}"); 
9    // 然后执行恢复逻辑
10    // ...
11}
12

3. TryParse vs. try-catch**

考虑一个场景:验证用户输入的字符串是否为有效的整数。我们有两种方法:

方法A: LBYL (Look Before You Leap) - 先看后跳使用 int.TryParse 进行预检查。

1string input = Console.ReadLine();
2if (int.TryParse(input, out int number))
3{
4    // 成功,使用 number
5    Console.WriteLine($"你输入的数字是: {number}");
6}
7else
8{
9    // 失败,处理无效输入
10    Console.WriteLine("无效的输入,请输入一个整数。");
11}
12

方法B: EAFP (It's Easier to Ask for Forgiveness than Permission) - 先做后问直接尝试转换,用 try-catch 处理失败情况。

1string input = Console.ReadLine();
2try
3{
4    int number = int.Parse(input);
5    // 成功,使用 number
6    Console.WriteLine($"你输入的数字是: {number}");
7}
8catch (FormatException)
9{
10    // 失败,处理无效输入
11    Console.WriteLine("无效的输入,请输入一个整数。");
12}
13

如何选择?

  1. 性能抛出和捕获异常是一个非常昂贵的操作。CLR需要保存当前执行状态,展开调用堆栈,搜索 catch 块等,这比一个简单的 if-else 判断要慢得多。因此,在性能敏感的代码或错误是“可预期的常规事件”(比如用户输入错误)时,TryParse 模式是首选
  2. 代码清晰度TryParse 明确地表达了“我正在尝试转换,并检查其结果”的意图,逻辑清晰。而使用 try-catch 来控制正常的程序流程,则被认为是一种反模式(anti-pattern),因为它混淆了“真正的异常情况”和“正常的逻辑分支”。
  3. 适用场景
    • 使用 TryParse:当失败是常见且可预期的分支时(如用户输入验证、检查字典中是否存在键)。
    • 使用 try-catch:当失败是真正“异常”的、不希望发生的情况时(如文件损坏、网络断开、磁盘已满)。

结论:不要用异常来控制程序流程。异常处理是为意外准备的,不是为日常准备的。


结语

点个赞,关注我获取更多实用 C# 技术干货!如果觉得有用,记得收藏本文


C#异常概念与try-catch入门》 是转载文章,点击查看原文


相关推荐


Redis(161)如何使用Redis实现分布式锁?
Victor3562025/12/3

分布式锁是用来在分布式系统下控制对资源的独占访问,确保多个进程或线程不会同时操作同一资源。Redis 是实现分布式锁的常用工具之一。下面是详细的实现步骤和代码示例: 1. Redis 分布式锁的基本实现 通过设置一个唯一的键来实现锁,当操作完成后删除该键即可释放锁。 1.1 获取锁 使用 SET 命令并设置过期时间。NX 参数表示只有在键不存在时才能设置成功,EX 参数用于设置键的过期时间。 SET lock_key unique_value NX EX 10 1.2 释放锁 只有持有锁的客户


单片机手搓掌上游戏机(十五)—pico运行fc模拟器之编译环境
Bona Sun2025/11/30

pico是arm内核的单片机,基本的还是gcc、make那些东西,另外有自己的pico-sdk和必须使用cmake生成makefile,当然也可以用msbuild或ninja代替make 流程无非是按照pico-sdk的说明把需要的工具链下载,然后设置环境变量,用vs code之类的ide编辑修改。链接: https://github.com/raspberrypi/pico-sdk 还是需要fastgithub之类的加速器,不然无法下载。 如果只是在windows下使用,还有一种更省事


大学生HTML期末大作业——HTML+CSS+JavaScript名侦探柯南
无·糖2025/11/27

HTML+CSS+JS【动漫网站】网页设计期末课程大作业 web前端开发技术 web课程设计 网页规划与设计 💥 文章目录一、🏁 网站题目二、🚩 网站描述三、🎌 网站介绍四、🏴 网站效果五、🏳️ 网站代码六、🏳️‍🌈 如何学习进步七、🏴‍☠️ 更多干货 💥 文章目录 一、🏁 网站题目 💪💪💪 动漫网站 名侦探柯南 5页 含网页设计报告 二、🚩 网站描述 🏅 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、


UI小姐姐要求有“Duang~Duang”的效果怎么办?
前端九哥2025/11/25

设计小姐姐: “搞一下这样的回弹效果,你行不行?” 我:“行!直接梭哈 50 行 keyframes + transform + 各种百分比,搞定 ” 设计小姐姐:“太硬(撇嘴),不够 Q 弹(鄙视)” 我:(裂开) 隔壁老王:这么简单你都不行,我来一行贝塞尔 cubic-bezier(0.3, 1.15, 0.33, 1.57) 秒了😎 设计小姐姐:哇哦!(兴奋)好帅!(星星眼🌟)好Q弹!(一脸崇拜😍) 我:“???” 🧠 一、为什么一行贝塞尔就能“Duang”起来? 1️⃣ c


精选 5 款 .NET 开源、实用的商城系统(Shop),快速商城二开利器!
追逐时光者2025/11/23

前言 今天大姚给大家分享 5 款基于 .NET 开源、实用的商城系统(Shop),快速商城二开利器!希望可以帮助到有商城系统开发需求的同学。 nopCommerce nopCommerce是一个.NET开源功能丰富、免费、灵活且可定制的开源电子商务解决方案(大家假如有商城需求可以直接使用该项目进行二次开发,省时省力) ,具备商城该有的各种功能和特性。 项目源码地址:github.com/nopSolution… 项目详细介绍:mp.weixin.qq.com/s/GMCIYwGUr… C


Python中的简单爬虫
q***57742025/11/21

文章目录 一. 基于FastAPI之Web站点开发 1. 基于FastAPI搭建Web服务器2. Web服务器和浏览器的通讯流程3. 浏览器访问Web服务器的通讯流程4. 加载图片资源代码 二. 基于Web请求的FastAPI通用配置 1. 目前Web服务器存在问题2. 基于Web请求的FastAPI通用配置 三. Python爬虫介绍 1. 什么是爬虫2. 爬虫的基本步骤3. 安装requests模块4. 爬取照片 ① 查看index.html② 爬取照片步骤③


Fastadmin切换英文,部分新增菜单没有切换原因
JSON_L2025/11/19

在使用Fastadmin开启多语言后,切换为英文时,部分新增菜单没有切换问题及解决。 目录 开启多语言 开启多语言 部分菜单没有变化 原因 添加菜单规则 语言包解析 其他标题或提示语解析 总结   开启多语言 开启多语言 首先介绍一下fastadmin中如何开启多语言配置, 在application/config.php中把lang_switch_on改为true。 默认情况下是关闭的,如下: 开启后,在后台会多一个切换语言的入口,默认就是中英


GitHub 热榜项目 - 日榜(2025-11-16)
CoderJia_2025/11/19

GitHub 热榜项目 - 日榜(2025-11-16) 生成于:2025-11-16 统计摘要 共发现热门项目: 18 个 榜单类型:日榜 本期热点趋势总结 本期GitHub热榜显示AI应用开发依然是绝对焦点,开发者正致力于将前沿AI技术转化为开箱即用的解决方案。舆情监控工具TrendRadar和谷歌的AI工具包adk-go引领了这股潮流,它们强调用AI解决信息过载和复杂流程自动化等实际问题。同时,轻量化RAG框架LightRAG和大模型记忆引擎Memori的流行,反映出社区


【深度学习新浪潮】算法工程师如何入门芯片硬软件设计工作?
Andrew-国星宇航2025/11/17

在AI与边缘计算爆发的当下,“算法落地难”逐渐成为行业痛点——很多时候不是算法不够优,而是缺乏匹配算法需求的芯片硬件支撑,或是算法与硬件“脱节”导致部署后性能打折扣。这也让越来越多算法工程师开始关注芯片硬软件设计:既能更深刻地理解算法的硬件适配性,又能参与到“算法-芯片”协同优化的核心环节,成为跨领域的复合型人才。 但算法工程师转型芯片设计,常面临“软件思维 vs 硬件思维”的鸿沟:习惯了软件的灵活性,却对硬件的并行性、时序约束、资源限制感到陌生;熟悉模型训练与推理优化,却不懂EDA工具、数字


python爬虫入门案例day05:Pexels
自学互联网2025/11/16

python爬虫入门案例day05:Pexels 目标网站 Pexels 目标网址 https://www.pexels.com/zh-cn/search/%E6%97%A5%E8%90%BD/ 开发环境 1、window11 2、python3.7 3、PyCharm Community Edition 2021.2.1 4、双核浏览器 5、浏览器自带开发者工具 网站分析 在下拉网页的过程中发现,会不断加载出新的图片,说明该网页为动态网页,那我们可以初步猜想图片的链接存放在通过Ajax请求的网

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2025 聚合阅读