百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

常见的C#异常及其修复方法

bigegpt 2024-08-19 11:58 2 浏览

常见的C#异常及其修复方法

如果您今天是依靠编写的软件来谋生,那么您可能至少对异常的概念很熟悉。

Jeff Atwood曾经称它们为“现代编程语言的基础”。异常[1]是现代软件开发中常见且有用的结构,但有时它们也可能造成混乱。

那么什么是异常?更具体地说,C#异常的主要类型是什么,以及如何使用它们?

今天的帖子将回答上述问题以及更多问题。我们将从“异常”的简要定义开始,然后继续解释该结构的组成部分。最后,我们将概述最常见的C#异常以及如何处理它们。

让我们开始吧。

有什么异常?

异常是一种可用于处理错误的机制。就这么简单。在某些情况下,例如,C程序员将返回错误代码,而Java[2]或C#程序员都很有可能引发异常。

异常表示执行流程的突然中断。一旦引发异常,执行就会停止。如果未处理异常,则应用程序崩溃。

但是,实际上您是如何做到的呢?您如何引发或捕获异常?所有这些甚至意味着什么?

这就是我们将在下一部分中详细介绍的内容。

C#异常剖析

现在,我们将简要介绍C#异常的情况。您将了解应该使用的主要关键字,这些关键字不仅可以捕获和处理异常,还可以抛出自己的异常。我们列表上的第一个是try关键字和块。

try

异常处理解剖的第一部分是try块。您可以使用它来尝试执行一些可能引发异常的代码。考虑以下代码摘录:

string content = string.Empty;try{    content = System.IO.File.ReadAllText(@"C:\file.txt");}

上面的代码声明了一个变量,并为其分配了空字符串。然后,我们有了try块。块中的单行代码try使用该类中的ReadAllText静态方法System.IO.File。我们担心该路径表示的文件可能不存在,在这种情况下会引发异常。但是,当然,这还不够。我们的代码不执行任何处理异常的操作。它甚至目前还没有编译!这是来自编译器的消息:


Expected catch or finally

该消息是不言自明的。我们需要 catch或finally代码块,因为尝试处理异常然后忘记执行处理部分没有任何意义。让我们开始吧。

catch

catch代码块使我们能够实际处理异常。我们将使用更多代码扩展前面的示例。

看看这个:

        static void Main(string[] args)        {            string content = string.Empty;            try            {                Console.WriteLine("First message inside try block.");                Console.WriteLine("Second message inside try block.");                content = System.IO.File.ReadAllText(@"C:\file.txt");                Console.WriteLine("Third message inside try block. This shouldn't be printed.");            }            catch            {                Console.WriteLine("First message inside the catch block.");                Console.WriteLine("Second message inside the catch block.");            }            Console.WriteLine("Outside the try-catch.");            Console.ReadLine();        }

如果运行上面的代码,则应看到以下内容:

First message inside try block.Second message inside try block.First message inside the catch block.Second message inside the catch block.Outside try-catch.

该路径处的文件不存在,因此将引发System.FileNotFoundException。发生这种情况时,执行流程将立即中断。因此,将在try块中打印“第三条消息”的行。这不应该被打印。” 永远不会被执行。

执行流程由catch块执行。完成后,将控制权交还给main方法,然后打印Outside try-catch。并等待用户输入。

最后

在了解了try和之后catch,我们终于(没有双关语)准备好使用这个非常有用但经常被误解的异常处理机制的一部分。那么,什么是finally?

该finally块是一种确保将执行给定代码段的方式,无论是否引发异常。考虑下面的代码:

try{    content = System.IO.File.ReadAllText(path);    Console.WriteLine("If you're reading this, no exception was thrown.");}catch (System.IO.FileNotFoundException e){    Console.WriteLine("If you're reading this, an exception was thrown.");    Console.WriteLine("Message: " + e.Message);}finally{    Console.WriteLine("This will be printed, no matter what.");}

那仍然是相同的示例,但是现在要简单得多。注意最后的finally块。如果运行此代码,则应该看到以下消息:

If you're reading this, an exception was thrown.Message: Could not find file 'C:\file.txt'.This will be printed, no matter what.Outside the try-catch.

现在让我们模拟文件的存在。注释掉试图从文件中读取的行,然后再次运行该应用程序。这是您现在应该看到的:


If you're reading this, no exception was thrown.This will be printed, no matter what.Outside the try-catch.

如您所见,在最近的场景中,没有引发异常,因此该catch块中没有执行任何行。另一方面,finally在两种情况下都执行了该块。

throw

当涉及到异常时,您将不会总是从别人那里处理它们。您也可以自己抛出异常。为此,您将使用throw关键字,然后是要引发的异常的类的实例化。以下代码举例说明了这一点:

public ProductService(IProductRepository repository){    if (repository == null)        throw new ArgumentNullException();    this.repository = repository;}

常见的.NET异常

以下是常见的.NET异常列表:

System.NullReferenceException

这是最著名的(甚至是臭名昭著的)异常之一。当您尝试调用方法/属性/索引器/等时,抛出此异常。在包含空引用的变量(即,它不指向任何对象)。下面的代码将导致空引用异常:

Person p = people.Where(x => x.SSN == verifySsn).FirstOrDefault();string name = p.Name;

在上面的示例中,我们过滤了将每个项目的SSN属性与verifySsnvariable变量进行比较的序列。然后,我们使用该FirstOrDefault()方法从序列中仅提取第一项。如果序列不产生任何项目,则它将返回该类型的默认值。由于Person是引用类型,因此其返回值为null。

在下一行,我们尝试Name在空引用上取消引用属性。Boom!这将空引用异常。

这是通常不抛出也不捕获的异常。您不要抛出它,因为它毫无意义。如果您想与代码的调用者交流,给定方法不接受null作为其参数的有效值,则使用的正确异常是System.ArgumentNullException。

您如何“修复”此异常?简而言之,您必须对可为空性小心谨慎。如果您正在编写将由第三方使用的任何代码(即使这些第三方是您的同事),则请认真考虑是否接受空引用作为有效值。

无论您做出什么决定,都必须使该决定非常明确并记录在案。您可以通过抛出System.ArgumentNullException,例如,并在方法上使用XML文档标题来实现。

System.IndexOutOfRangeException

在应用程序代码通常不会抛出或捕获该异常的意义上,该异常与上一个异常类似。

那为什么呢?

好吧,当您尝试使用无效的索引值访问数组,列表或任何可索引序列中的元素时,将引发此异常。一个简单的例子:

public static void PrintUrlSufix(string url){    var parts = url.Split('.');    Console.WriteLine(parts[2]);}

上面的代码显然希望使用www.acme.com[3]格式的URL 。但是,如果只是获得acme.com怎么办?为此,如果得到Hakuna Matata怎么办?是的,没错:System.IndexOutOfBoundException是的。

您如何避免遇到此异常?永远不要把事情视为理所当然。永远不要仅仅假设数据将采用正确的格式。做您的尽职调查和检查的东西。

System.IO.IOException

此C#异常具有一个不言自明的名称。这正是您的想法:这是IO操作期间发生错误时引发的异常。与前两个异常不同,您可能会发现自己不时捕捉或抛出其中一个。

本IOException类实际上是一些更具体的异常,例如:

?DirectoryNotFoundException?EndOfStreamException?FileNotFoundException?FileLoadException?PathTooLongException

有关的文档,IOException建议您尽可能使用更具体的异常,而不是更一般的异常。

System.Net.WebException

此异常与网络有关。如果使用可插拔协议[4]访问网络时发生错误,则抛出该错误。[5]处理此异常时,请记住验证该Response属性,该属性将包含远程主机返回的响应。

System.Data.SqlClient.SqlException

此异常与数据库(特别是SQL Server)有关。SQL Server返回错误或警告时将引发该错误。该类具有一个称谓的属性Errors,该属性是一个包含SqlError该类的一个或多个实例的集合。依次包含有关发生的错误的详细信息。

System.StackOverflowException

当执行堆栈溢出时,抛出此异常,这通常意味着递归出错。该代码有太多的嵌套方法调用。事情就是这样:这个异常是无法捕获的-至少从.NET 2.0起就没有-这意味着当抛出该异常时,您几乎没有其他选择。默认情况下,您的过程将被终止。

您应该做的而不是捕获此异常的方法是编写代码,以防止它首先发生。

System.OutOfMemoryException

可以说这是最令人困惑的C#异常之一[6]。网上有很多资源可以很好地阐明问题[7],但是我将在此处提供一个简短的版本。发生的情况是此异常不涉及可用的物理内存。

那么,什么时候抛出此异常?

您知道何时要停车吗,因为其他驾驶员未正确停车而不能停车吗?如果您只需在停放的汽车之间添加所有可用空间,就足以容纳您的车辆。但是目前不可能,因为它不是连续的区域。

当您获得此内存时,这差不多发生了什么。可能有很多可用的总内存,但是没有连续的部分可以满足所需的分配。实际上,例如,如果您尝试将 StringBuilder的MaxCapacity属性扩展到该属性之外,则会发生此异常。

System.InvalidCastException

此异常也具有不言自明的名称。当代码由于未定义强制类型转换而无法从一种类型转换为另一种类型时,将引发该错误。以下代码将引发此类型的异常:



object o = "10";int x = (int)o;

这是您通常不会捕获的异常。相反,您将以不会发生的方式编写代码。例如,以下代码在尝试强制类型转换之前进行类型检查:









public override bool Equals (object obj ){   if (!obj is Foo)       return false;   Foo other = (Foo)obj;   return this.bar == other.bar;}

上面的代码可以使用简化的as操作符[8],在这里我没有进一步具体说明,作为一个练习留给读者。无论如何,这就是问题:您实际上应该尝试做的是避免问题,而不是首先进行转换。利用泛型来防止陷入需要强制转换的情况。

System.InvalidOperationException

像之前的许多其他异常一样,这种异常通常是您不会发现的。相反,您应该做的是编写不会发生的代码。考虑以下示例:



var numbers = new List<int> { 1, 3, 5 };var firstGreaterThanFive = numbers.Where(x => x > 5).First();

当序列不产生任何结果时,将抛出First LINQ扩展方法。如果您知道该序列有时可能不会产生结果-没关系-您应该改用FirstOrDefault。在空序列上调用此方法时,将返回序列类型的默认值,而不是抛出异常。

System.ObjectDisposedException

我们将在这篇文章中介绍的最后一个C#异常也属于“不应该处理,请修复代码”类别。换句话说,这是开发人员错误。当您尝试使用已处理的IDisposable[9]进行操作时,将引发此异常。

此当通常发生在开发者调用Dispose,Close或其它类似方法和后来试图访问该对象的一个成员时。

如何成功的进行异常处理?

错误处理是软件开发教学中经常被忽略的话题,出现异常有时非常不幸。如果没有可靠的错误处理策略[10],您的应用程序有可能质量会不过关。

通过本文,我们希望通过定义异常的概念并对C#异常的主要类型进行快速概述,以帮助解决该问题。但本文并没有涵盖异常处理的全部。恰好相反,我认为这是一个机会,可以开始引导您对该主题的学习,并且永不停止学习和练习。

祝您能想出一种对您的应用程序有效的策略!

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...