当前位置: 首页 > article >正文

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

一、什么是异常?

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

常见的异常类型:

  • 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.kler.cn/a/430551.html

相关文章:

  • WordPress网站中如何修复504错误
  • 自学记录HarmonyOS Next的HMS AI API 13:语音合成与语音识别
  • 大模型的实践应用33-关于大模型中的Qwen2与Llama3具体架构的差异全解析
  • 地理数据库Telepg面试内容整理-请描述空间索引的基本概念,如何使用它提高查询性能
  • Java全栈项目 - 学生竞赛管理平台
  • FlaskAPI-初识
  • 在多个分布式机器间设置和使用 NFS(Network File System)共享目录的步骤如下:
  • 家校通小程序实战教程06口令验证
  • ArrayBuffer,TypedArray,Int8Array 和Blob的关系
  • python爬虫常用数据保存模板(Excel、CSV、mysql)——scrapy中常用数据提取方法(CSS、XPATH、正则)(23)
  • EFCore PostgreSQL在.NET9生成迁移文件错误
  • 【前端】浏览器输入url到页面呈现发生了什么?
  • csrf漏洞复现
  • Copilot for Microsoft 365 Plugins 示例项目教程
  • FM25V20A-DGQ:耐用、快速、低功耗的F-RAM
  • kcat - Apache Kafka producer and consumer tool
  • 调度系统:基于 Couchbase 构建数仓 Temporal、Apache Airflow 和 DonpinScheduler 的详细比较
  • IdentityServer4框架、ASP.NET core Identity
  • ios使用UIScrollView和PageControl创建图片轮播
  • selenium学习:等待方式
  • 网络安全法-网络运行安全
  • Scala正则表达式
  • UAC2.0 speaker——带反馈端点的 USB speaker(16bit 单声道)
  • 大数据新视界 -- Hive 临时表与视图的应用场景(下)(30 / 30)
  • 机器学习 (西瓜书) 内容概要【不含数学推导】
  • MySQL 通过 Next-Key Locking 技术避免幻读问题