C# 异常处理全解析:让程序告别崩溃噩梦

server/2024/12/14 18:20:21/

一、什么是异常?

异常是程序在运行过程中发生的错误或意外情况。当程序遇到无法正常执行的情形时,会抛出一个异常,以通知程序发生了错误。异常是程序中的一种事件,需要被捕获处理,否则程序可能会崩溃或终止运行。

常见的异常类型:

  • DivideByZeroException:除数为零异常
  • NullReferenceException:空引用异常
  • IndexOutOfRangeException:索引超出范围异常
  • FileNotFoundException:文件未找到异常

二、为什么需要异常处理?

在编写程序时,我们无法完全避免错误的发生。例如,用户可能会输入不合适的数据,网络连接可能会中断,文件可能会丢失等。如果不处理这些异常,程序会直接崩溃,给用户带来不好的体验。

通过异常处理,我们可以:

  • 捕获异常:检测到错误时,不让程序立刻崩溃。
  • 处理异常:采取措施应对异常,使程序能够继续运行或友好地退出。
  • 提供反馈:向用户提供有用的错误信息,便于调试和改进程序。

三、异常处理的关键字

C#中异常处理主要使用以下关键字:

  • try:放置可能产生异常的代码块。
  • catch:捕获异常,并对其进行处理。
  • finally:无论是否发生异常,都会执行的代码块。
  • throw:主动抛出一个异常。

四、异常处理的基本结构

基本结构:

try
{// 可能产生异常的代码
}
catch (ExceptionType ex)
{// 对异常进行处理的代码
}
finally
{// 无论是否发生异常,都会执行的代码
}

说明:

  • try块中包含可能会引发异常的代码。
  • catch块用于捕获特定类型的异常,并对其进行处理。
  • finally块是可选的,用于执行清理操作,如关闭文件、释放资源等。

五、实例解析异常和异常处理

1. 未处理异常的情况

using System;class Program
{static void Main(){int a = 10;int b = 0;int result = a / b; // 除以零,抛出异常Console.WriteLine("结果是:" + result);}
}

运行结果:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.

程序由于除以零,抛出了DivideByZeroException,并崩溃了。


2. 使用异常处理

using System;class Program
{static void Main(){int a = 10;int b = 0;try{int result = a / b; // 可能引发异常的代码Console.WriteLine("结果是:" + result);}catch (DivideByZeroException ex){Console.WriteLine("发生错误:不能除以零!");// 可以根据需要输出异常信息// Console.WriteLine(ex.Message);}}
}

运行结果:

发生错误:不能除以零!

说明:

  • try块中包含可能发生异常的代码int result = a / b;
  • b为零时,抛出DivideByZeroException
  • catch块捕获到该异常,并执行其中的代码,输出提示信息。
  • 程序没有崩溃,可以继续执行后续的代码。

3. 使用finally

using System;
using System.IO;class Program
{static void Main(){StreamReader sr = null;try{sr = new StreamReader("test.txt");string content = sr.ReadToEnd();Console.WriteLine(content);}catch (FileNotFoundException ex){Console.WriteLine("文件未找到!");}finally{// 无论是否发生异常,都要关闭文件if (sr != null){sr.Close();Console.WriteLine("文件已关闭。");}}}
}

说明:

  • try块尝试读取文件test.txt
  • 如果文件不存在,会抛出FileNotFoundException,并被catch块捕获。
  • finally块中的代码会在trycatch块执行完后执行,用于关闭文件等清理操作。
  • 即使发生异常,finally块仍然会执行,确保资源被正确释放。

4. 使用throw关键字

throw关键字用于主动抛出异常。

using System;class Program
{static void Main(){try{CheckAge(15);}catch (Exception ex){Console.WriteLine("错误:" + ex.Message);}}static void CheckAge(int age){if (age < 18){throw new Exception("未成年人禁止入内!");}else{Console.WriteLine("欢迎光临!");}}
}

运行结果:

错误:未成年人禁止入内!

说明:

  • CheckAge方法中,如果age小于18,主动throw一个Exception
  • Main方法中,调用CheckAge(15),由于age为15,抛出异常。
  • catch块捕获到异常,并输出错误信息。

六、自定义异常

有时候,内置的异常类型不能满足需求,我们可以自定义异常类。

示例:

using System;class Program
{static void Main(){try{Withdraw(1000, 1500);}catch (InsufficientFundsException ex){Console.WriteLine("错误:" + ex.Message);}}static void Withdraw(double balance, double amount){if (amount > balance){throw new InsufficientFundsException("余额不足,无法取款!");}else{balance -= amount;Console.WriteLine("取款成功,余额为:" + balance);}}
}// 自定义异常类
public class InsufficientFundsException : Exception
{public InsufficientFundsException(string message) : base(message){}
}

运行结果:

错误:余额不足,无法取款!

说明:

  • 定义了一个自定义异常类InsufficientFundsException,继承自Exception
  • Withdraw方法中,如果取款金额大于余额,抛出InsufficientFundsException
  • catch块捕获自定义异常,并输出错误信息。

七、异常的捕获顺序

当有多个catch块时,异常的捕获顺序是从上到下匹配。如果基类异常(如Exception)放在前面,那么子类异常将无法被捕获。

示例:

try
{// 可能产生多种异常的代码
}
catch (DivideByZeroException ex)
{// 处理除以零异常
}
catch (Exception ex)
{// 处理其他异常
}

注意:

  • 特定异常(如DivideByZeroException)的catch块应放在前面。
  • 通用异常(如Exception)的catch块应放在后面。

八、小结

  • 异常是程序运行时发生的错误或意外情况,需要被捕获和处理。
  • 使用trycatchfinallythrow关键字进行异常处理。
  • try:包含可能发生异常的代码。
  • catch:捕获并处理异常。
  • finally:无论是否发生异常,都会执行,用于清理资源。
  • throw关键字:主动抛出异常。
  • 自定义异常:当内置异常类型不满足需求时,可以创建自己的异常类。
  • 捕获顺序:从特定异常到通用异常,catch块的顺序很重要。

九、建议

  • 尽早捕获异常:在可能发生异常的地方,及时使用try-catch
  • 合理处理异常:在catch块中,提供有用的错误信息或采取适当的措施。
  • 避免过度捕获:不要将整个程序都放在一个大的try-catch中。
  • 资源清理:使用finally块或using语句,确保资源被正确释放。

十、动手实践

以下是三道关于异常处理的操作题,希望能帮助你更好地理解异常处理的概念和实践:

问题1:

题目描述:

编写一个控制台应用程序,要求用户输入一个整数,然后计算该整数的平方根。如果用户输入的不是有效的整数,或者输入的数字为负数,程序应当捕获异常并提示用户输入正确的值。

要求:

  • 使用异常处理机制捕获以下情况:
    • 用户输入的不是一个整数(FormatException)。
    • 用户输入的整数为负数(自定义异常或处理逻辑)。
  • 在捕获异常后,向用户输出友好的错误提示信息,并允许用户重新输入。

提示:

  • 使用int.Parse()Convert.ToInt32()方法将字符串转换为整数。
  • 使用try-catch块捕获格式异常。
  • 当数值为负数时,可以手动抛出一个ArgumentOutOfRangeException或自定义异常。
  • 使用循环使用户在输入错误时可以重新输入。

问题2:

题目描述:

编写一个程序,从一个文本文件data.txt中读取数据并输出到控制台。请在代码中进行异常处理,防止以下情况导致程序崩溃:

  • 文件未找到(FileNotFoundException)。
  • 无法访问文件,例如权限不足(UnauthorizedAccessException)。
  • 在读取文件内容时发生其他I/O错误(IOException)。

要求:

  • 对不同类型的异常,提供不同的错误提示信息。
  • 使用finally块或using语句,确保在程序结束前文件被正确关闭或资源被释放。
  • 如果读取成功,输出文件的内容。

提示:

  • 使用System.IO命名空间下的StreamReaderFile.ReadAllText()方法读取文件。
  • 使用多个catch块分别捕获不同的异常类型。
  • finally块用于执行清理操作,无论是否发生异常。

问题3:

题目描述:

创建一个自定义异常类NegativeNumberException,用于表示遇到了负数的异常情况。

编写一个方法CalculateFactorial(int n),用于计算整数n的阶乘:

  • 如果n是负数,抛出NegativeNumberException
  • 如果计算过程中发生溢出(OverflowException),捕获异常并提示用户。

编写主程序:

  • 要求用户输入一个非负整数。
  • 调用CalculateFactorial(n)计算并输出结果。
  • 使用异常处理机制,捕获并处理可能发生的异常,包括自定义异常和溢出异常。
  • 在捕获异常后,向用户输出友好的错误信息。

提示:

  • 自定义异常需要继承自Exception类,并实现相应的构造函数。
  • 在计算阶乘时,可以使用checked关键字块来检测溢出。
  • 使用try-catch结构捕获异常。

注意: 完成这些练习有助于你深入理解C#中的异常处理机制,包括如何捕获和处理不同类型的异常,以及如何编写健壮的代码来提高程序的可靠性。


http://www.ppmy.cn/server/150152.html

相关文章

使用HashMap实现LRU

1. 使用LinkedList实现 import java.util.LinkedList;public class Main {public static void main(String[] args) throws Exception {cacheLRU cache new cacheLRU(3);cache.add(1);cache.add(2);cache.add(3);System.out.print(cache.get(1));System.out.print(cache.get(…

EXCEL 数据透视表基础操作

目录 1 选择数据&#xff0c;插入数据透视表 2 选择数据透视表生成位置 3 出现了数据透视表的面板 4 数据透视表的基本结构认识 4.1 交叉表/列联表 4.2 row, column, cell 一个新增的筛选器&#xff0c;就这么简单 4.3 可以只添加 rowcell/值 &#xff0c;也可以colu…

【源码+文档+调试讲解】校园零售商城微信小程序

摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括校园零售商城微信小程序的网络应用&#xff0c;在外国校园零售商城微信小程序已经是很普遍的方式&#xff0c;不过国内的校园零售商城微信小程序可能还处于起步阶段。校…

网络爬虫全解析

一、网络爬虫基础要点 &#xff08;一&#xff09;爬虫原理 目标确定&#xff1a;明确需要抓取数据的网站或网页范围&#xff0c;例如针对特定电商平台抓取商品信息&#xff0c;或聚焦新闻网站获取新闻报道内容&#xff0c;要考量数据的价值与用途。URL 解析&#xff1a;理解网…

【深度学习入门】深度学习介绍

1.1 深度学习介绍 学习目标 目标 知道深度学习与机器学习的区别了解神经网络的结构组成知道深度学习效果特点 应用 无 区别 特征提取方面 机器学习的特征工程步骤是要靠手动完成的&#xff0c;而且需要大量领域专业知识深度学习通常由多个层组成&#xff0c;它们通常将更简…

QoS分类和标记

https://zhuanlan.zhihu.com/p/160937314 1111111 分类和标记是识别每个数据包优先级的过程。 这是QoS控制的第一步&#xff0c;应在源主机附近完成。 分组通常通过其分组报头来分类。下图指定的规则仔细检查了数据包头 &#xff1a; 下表列出了分类标准&#xff1a; 普通二…

ip地址获取失败啥意思?ip地址获取失败怎么回事

在日常的网络使用中&#xff0c;我们时常依赖于稳定的IP地址来确保数据的顺畅传输和设备的正常识别。然而&#xff0c;有时我们会遇到“IP地址获取失败”的困扰&#xff0c;这不仅阻碍了我们的网络访问&#xff0c;还可能带来一系列的网络连接问题。那么&#xff0c;IP地址获取…

Java多线程与线程池技术详解(九)

面对苦难的态度&#xff1a;《病隙碎笔》“不断的苦难才是不断地需要信心的原因&#xff0c;这是信心的原则&#xff0c;不可稍有更动。” 孤独与心灵的成长&#xff1a;《我与地坛》“孤独的心必是充盈的心&#xff0c;充盈得要流溢出来要冲涌出去&#xff0c;便渴望有人呼应他…