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

编程语言的四种错误处理方法,你知道几种?

     错误处理是编程的一个基本要素。除非你写的是“hello world”,否则就必须处理代码中的错误。在本文中,我将讨论各种编程语言在处理错误时使用的最常见的四种方法,并分析它们的优缺点。

关注不同设计方案的语法、代码可读性、演变过程、运行效率,将有助于我们写出更为优雅和健壮的代码。

返回错误代码

这是最古老的策略之一——如果一个函数可能会出错,它可以简单地返回一个错误代码——通常是负数或者null。例如,C 语言中经常使用:

FILE* fp = fopen("file.txt" , "w");if (!fp) {// 发生了错误}

这种方法非常简单,既易于实现,也易于理解。它的执行效率也非常高,因为它只需要进行标准的函数调用,并返回一个值,不需要有运行时支持或分配内存。但是,它也有一些缺点:

用户很容易忘记处理函数的错误。例如,在 C 中,printf 可能会出错,但我几乎没有见过程序检查它的返回值!

如果代码必须处理多个不同的错误(打开文件,写入文件,从另一个文件读取等),那么传递错误到调用堆栈会很麻烦。

除非你的编程语言支持多个返回值,否则如果必须返回一个有效值或一个错误,就很麻烦。这导致 C C++ 中的许多函数必须通过指针来传递存储了成功返回值的地址空间,再由函数填充,类似于:

my_struct *success_result;int error_code = my_function(&success_result);if (!error_code) {// can use success_result}

众所周知,Go 选择了这种方法来处理错误,而且,由于它允许一个函数返回多个值,因此这种模式变得更加人性化,并且非常常见:

user, err = FindUser(username)if err != nil {return err}

Go 采用的方式简单而有效,会将错误传递到调用方。但是,我觉得它会造成很多重复,而且影响到了实际的业务逻辑。不过,我写的 Go 还不够多,不知道这种印象以后会不会改观!

异常

异常可能是最常用的错误处理模式。try/catch/finally 方法相当有效,而且使用简单。异常在上世纪 90 年代到 2000 年间非常流行,被许多语言所采用(例如 JavaC# 和 Python)。

与错误处理相比,异常具有以下优点:

它们自然地区分了快乐路径和错误处理路径

它们会自动从调用堆栈中冒泡出来

你不会忘记处理错误!

然而,它们也有一些缺点:需要一些特定的运行时支持,通常会带来相当大的性能开销。

此外,更重要的是,它们具有深远的影响——某些代码可能会抛出异常,但被调用堆栈中非常远的异常处理程序捕获,这会影响代码的可读性。

此外,仅凭查看函数的签名,无法确定它是否会抛出异常。

C++ 试图通过throws 关键字来解决这个问题,但它很少被使用,因此在 C++ 17 中已被弃用 ,并在 C++ 20 中被删除。此后,它一直试图引入noexcept 关键字,但我较少写现代 C++,不知道它的流行程度。

(注:throws 关键字很少使用,因为使用过于繁琐,需要在函数签名中指定抛出的异常类型,并且这种方法不能处理运行时发生的异常,有因为未知异常而导致程序退出的风险)

Java 曾试图使用受检的异常(checked exceptions,即你必须将异常声明为函数签名的一部分——但是这种方法被认为是失败的,因此像 Spring 这种现代框架只使用运行时异常,而有些 JVM 语言(如 Kotlin)则完全抛弃了这个概念。这造成的结果是,你根本无法确定一个函数是否会抛出什么异常,最终只得到了一片混乱。

(注:Spring 不使用受检的异常,因为这需要在函数签名及调用函数中显式处理,会使得代码过于冗长而且造成不必要的耦合。使用运行时异常,代码间的依赖性降低了,也便于重构,但也造成了异常源头的混乱)

回调函数

另一种方法是在 JavaScript 领域非常常见的方法——使用回调,回调函数会在一个函数成功或失败时调用。这通常会与异步编程结合使用,其中 I/O 操作在后台进行,不会阻塞执行流。

例如,Node.JS I/O 函数通常加上一个回调函数,后者使用两个参数(errorresult),例如:

const fs = require('fs');fs.readFile('some_file.txt', (err, result) => {if (err) {console.error(err);return;}console.log(result);});

但是,这种方法经常会导致所谓的回调地狱问题,因为一个回调可能需要调用其它的异步 I/O,这可能又需要更多的回调,最终导致混乱且难以跟踪的代码。

现代的 JavaScript 版本试图通过引入promise 来提升代码的可读性:

fetch("https://example.com/profile", {method: "POST", // or 'PUT'}).then(response => response.json()).then(data => data['some_key']).catch(error => console.error("Error:", error));

promise 模式并不是最终方案,JavaScript 最后采用了由 C#推广开的 async/await 模式,它使异步 I/O 看起来非常像带有经典异常的同步代码:

async function fetchData() {try {const response = await fetch("my-url");if (!response.ok) {throw new Error("Network response was not OK");}return response.json()['some_property'];} catch (error) {console.error("There has been a problem with your fetch operation:", error);}}

使用回调进行错误处理是一种值得了解的重要模式,不仅仅在 JavaScript 中如此,人们在 C 语言中也使用了很多年。但是,它现在已经不太常见了,你很可能会用的是某种形式的async/await

函数式语言的 Result

我最后想要讨论的一种模式起源于函数式语言,比如 Haskell,但是由于 Rust 的流行,它已经变得非常主流了。

它的创意是提供一个Result类型,例如:

enum Result<S, E> {Ok(S),Err(E)}

这是一个具有两种结果的类型,一种表示成功,另一种表示失败。返回结果的函数要么返回一个Ok 对象(可能包含有一些数据),要么返回一个Err 对象(包含一些错误详情)。函数的调用者通常会使用模式匹配来处理这两种情况。

为了在调用堆栈中抛出错误,通常会编写如下的代码:

let result = match my_fallible_function() {Err(e) => return Err(e),Ok(some_data) => some_data,};

由于这种模式非常常见,Rust 专门引入了一个操作符(即问号 ?) 来简化上面的代码:

let result = my_fallible_function()?;   // 注意有个"?"号

这种方法的优点是它使错误处理既明显又类型安全,因为编译器会确保处理每个可能的结果。

在支持这种模式的编程语言中,Result 通常是一个 monad,它允许将可能失败的函数组合起来,而无需使用 try/catch 块或嵌套的 if 语句。

(注:函数式编程认为函数的输入和输出应该是纯粹的,不应该有任何副作用或状态变化。monad 是一个函数式编程的概念,它通过隔离副作用和状态来提高代码的可读性和可维护性,并允许组合多个操作来构建更复杂的操作)

根据你使用的编程语言和项目,你可能主要或仅仅使用其中一种错误处理的模式。

不过,我最喜欢的还是 Result 模式。当然,不仅是函数式语言采用了它,例如,在lastminute.com 中,我们在 Kotlin 中使用了 Arrow 库,它包含一个受 Haskell 强烈影响的类型Either

                                                              --END--

相关文章:

编程语言的四种错误处理方法,你知道几种?

错误处理是编程的一个基本要素。除非你写的是“hello world”&#xff0c;否则就必须处理代码中的错误。在本文中&#xff0c;我将讨论各种编程语言在处理错误时使用的最常见的四种方法&#xff0c;并分析它们的优缺点。 关注不同设计方案的语法、代码可读性、演变过程、运行效…...

ContOS7单机安装Hadoop

安装Hadoop 1&#xff0c;准备环节 因为Hadoop是由java编写的&#xff0c;所以需要Java的环境支持&#xff0c;作为开发者我们需要安装jdk。 安装jdk的教程http://t.csdn.cn/6qJKg 下载Hadoop的安装包 Hadoop官网&#xff1a;http://hadoop.apache.org/ Hadoop版本下载地…...

抓取动态网页的数据的具体操作方法

抓取动态网页的数据的具体操作方法 动态网页是指在用户交互过程中&#xff0c;网页内容不断更新和变化的网页。抓取动态网页的数据需要了解以下具体操作方法&#xff1a; 使用浏览器开发者工具&#xff1a;在浏览器中打开目标网页后&#xff0c;按下F12键&#xff0c;打开开发…...

Windows 和 Linux 环境下 ProtoBuf 的安装

文章目录 一、ProtoBuf 在 Windows 环境中的安装二、ProtoBuf 在 Linux 环境中的安装 ProtoBuf在GitHub上的下载地址 一、ProtoBuf 在 Windows 环境中的安装 首先选择自己要下载的版本&#xff0c;我选择的是v21.11&#xff1a; 点进去在最下面选择Windows的版本&#xff0…...

商用密码应用安全性测评方案编制流程

密评方案编制的目标是完成测评准备活动中获取的信息系统相关资料整理&#xff0c;为现场测评活动提供最基本的文档和指导方案。 按照《GM-T 0116-2021 信息系统密码应用测评过程指南》标准&#xff0c;密评方案编制包括5项关键任务&#xff0c;简要汇总如下表。 编号任务输入文…...

Elasticsearch 集群部署插件管理及副本分片概念介绍

Elasticsearch 集群配置版本均为8以上 安装前准备 CPU 2C 内存4G或更多 操作系统: Ubuntu20.04,Ubuntu18.04,Rocky8.X,Centos 7.X 操作系统盘50G 主机名设置规则为nodeX.qingtong.org 生产环境建议准备单独的数据磁盘主机名 #各自服务器配置自己的主机名 hostnamectl set-ho…...

Liunx 套接字编程(2)TCP接口通信程序

1.TCP通信程序的编写 面向连接、可靠传输、提供字节流传输服务 客户端向服务器发送一个连接建立的请求流程&#xff0c;上图中服务端第三步详细流程 2.TCP接口 socket--创建套接字 int socket(int domain, int type, int protocol); bind---绑定 intbind(int sockfd, struct s…...

8年开发经验,浅谈 API 管理

随着信息化飞速增长的还有各信息系统中的应用接口&#xff08;API&#xff09;&#xff0c;API作为信息系统内部及不同信息系统之间进行数据传输的渠道&#xff0c;其数量随着软件系统的不断庞大而呈指数型增长&#xff0c;如何管理这些API已经在业界变得越来越重要&#xff0c…...

【软考备战·四月模考】希赛网四月模考软件设计师上午题

文章目录 一、成绩报告二、错题总结第一题第二题第三题第四题第五题第六题第七题第八题第九题第十题第十一题第十二题第十三题第十四题第十五题第十六题第十七题第十八题第十九题第二十题第二十一题第二十二题 三、知识查缺 题目及解析来源&#xff1a;2023上半年软考-模考大赛…...

MySQL中的@i:=@i+1用法详解

在MySQL中&#xff0c;i:i1是一个非常有用的表达式&#xff0c;用于在查询中生成一个递增的序列号。它可以帮助我们对结果进行编号&#xff0c;或者在需要连续的数字序列时提供便利。 我们先来了解一下MySQL中的用户变量。用户变量是一个用户定义的变量&#xff0c;其以开头。…...

web安全第一天 ,域名,dns

第一天 什么是域名&#xff1f;域名就是网络地址 在hhtp之后的就是域名 域名在哪里注册呢 国内注册商有很多&#xff0c;在网络上搜索一下阿里云万网就可以注册 什么是二级域名和多级域名 域名通常都是www.开头 &#xff0c;而www.被称为顶级域名&#xff0c;在搜索的时候…...

【Linux】Linux编辑神器vim的使用

目录 一、Vim的基本概念 二、Vim的基本操作 1、进入vim 2、正常模式切换至插入模式 3、插入模式切换至正常模式 4、正常模式切换至底行模式 5、退出Vim编辑器 三、Vim正常模式命令集 1、移动光标 2、删除文字 3、复制 4、替换 5、撤销 四、Vim底行模式命令集 1、列出行号 2、光…...

vulnhub渗透测试靶场练习1

靶场介绍 靶场名&#xff1a;Medium_socialnetwork 下载地址&#xff1a;https://www.vulnhub.com/entry/boredhackerblog-social-network,454/ 环境搭建 靶机建议选择VM VirtualBox,我一开始尝试使用VMware时会报错&#xff0c;所以改用VM VirtualBox&#xff0c;攻击机使用…...

Uart,RS232,RS485串口通讯协议学习

目录 定义 UART&#xff08;通常被称为串口,简单意味着使用广泛&#xff0c;具有普适性) RS232 RS232电平转换 RS485 -Recommended Standard (再推荐标准) 485和232的对比 RS485组网 总结 定义 串口是我们都很熟悉的&#xff0c;尤其是需要串口调试的时候,打印信息插…...

UML中的assembly关系

UML中的assembly关系 1.什么是Assembly关系 在UML&#xff08;统一建模语言&#xff09;中&#xff0c;"assembly"&#xff08;组装&#xff09;是一种表示组件之间关系的关联关系。组件是系统中可替换和独立的模块&#xff0c;可以通过组装来构建更大的系统。 当一…...

[Python]缓存cachetools与TTLCache简介

文章目录 cachetools缓存策略缓存操作 TTLCache cachetools是一个Python第三方库&#xff0c;提供了多种缓存算法的实现。 cachetools 使用前需要先安装pip install cachetools&#xff0c;说明文档参见https://cachetools.readthedocs.io/en/latest/。 cachetools提供了五种…...

现在的00后,真是卷死了呀,辞职信已经写好了·····

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;三月份春招我们公司来了个00后&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪23K&#xff0c;都快接近我了。 后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了…...

【wpf】列表类,用相对源时,如何绑定到子项

前言 在之前的一篇文章 &#xff1a;《【wpf】深度解析&#xff0c;Binding是如何寻找数据源的》https://blog.csdn.net/songhuangong123/article/details/126195727#:~:text%E3%80%90wpf%E3%80%91%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90%EF%BC%8CBinding%E6%98%AF%E5%A6%82%E4…...

头歌计算机组成原理实验—运算器设计(3)第3关:4位快速加法器设计

第3关&#xff1a;4位快速加法器设计 实验目的 帮助学生掌握快速加法器中先行进位的原理&#xff0c;能利用相关知识设计4位先行进位电路&#xff0c;并利用设计的4位先行进位电路构造4位快速加法器&#xff0c;能分析对应电路的时间延迟。 视频讲解 实验内容 利用前一步设…...

Java中synchronized的优化

本文介绍为了实现高效并发&#xff0c;虚拟机对 synchronized 做的一系列的锁优化措施 高效并发是从 JDK5 升级到 JDK6 后一项重要的改进项&#xff0c;HotSpot 虚拟机开发团队在 JDK6 这个版本上花费了大量的资源去实现各种锁优化技术&#xff0c;如适应性自旋&#xff08;Ada…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...