iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Swift语言中怎么自定义操作符
  • 155
分享到

Swift语言中怎么自定义操作符

2023-06-28 00:06:35 155人浏览 安东尼
摘要

这篇文章主要介绍了Swift语言中怎么自定义操作符,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在Swift语言中,常见的操作符有+、-、*、/、>、、==、&

这篇文章主要介绍了Swift语言中怎么自定义操作符,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

在Swift语言中,常见的操作符有+-*/>==&&||等等,如果不喜欢,你也可以定义自己喜欢的操作符。

Swift语言中怎么自定义操作符

数字容器

有时我们定义了实质上只是容器的值类型其容纳着更加原始的值。例如,在一个战略游戏中,玩家可以收集两种资源 ——木材和金币。要在代码中建模这些资源,我使用作为木材和金币值的容器的 Resource 结构体,如下所示:

struct Resources {   var Gold: Int   var wood: Int}

每当我引用一组资源时,我就会使用此结构 —— 例如,要跟踪玩家当前可用的资源:

struct Player {   var resources: Resources}

您可以在游戏中花费资源的一件事是为您的军队培训新单位。执行此类动作时,我只需从当前的玩家的资源中减去该单元的金币和木材成本:

func trainUnit(ofKind kind: Unit.Kind) {   let unit = Unit(kind: kind)   board.add(unit)   currentPlayer.resources.gold -= kind.cost.gold   currentPlayer.resources.wood -= kind.cost.wood}

做到上面的完全有效,但由于游戏中有许多影响玩家资源的动作,代码中有许多地方必须重复金币和木头的两个减法。

这不仅使得很容易忘记减少其中一个值,同时它还使得引入一种新的资源类型更难(例如,银币),因为我必须通过查看整个代码并更新所有处理资源的地方。

操作符重载

让我们尝试使用操作符重载来解决上述问题。使用大多数语言(包括Swift)的操作符时,您有都有两个选项,重载现有运算符,或者创建一个新的运算符。重载工作就像方法重载,您可以使用新的输入或输出创建新版本的操作符。

在这种情况下,我们将定义-=运算符的过载,它们适用于两个 Resources 值,如下所示:

extension Resources {   static func -=(lhs: inout Resources, rhs: Resources) {       lhs.gold -= rhs.gold       lhs.wood -= rhs.wood   }}

就像遵守 Equatable 协议的时候一样,Swift 中的操作符重载只是可以在类型上声明的一个正常静态函数。在此处 -= 中,操作符的左侧是一个 inoiut 参数,这是我们要修改的值。

通过我们的操作符重载,我们现在可以直接在当前的玩家的资源上简单地调用 -= ,就像我们将其放在在任何原始数值上:

currentPlayer.resources -= kind.cost

这不仅很好阅读,它还有助于我们消除代码重复问题。由于我们总是希望所有外部逻辑修改完整的 Resource 实例,因此我们可以将金币 gold 和木材 wood 属性作为只读属性开放给外部其他类:

struct Resources {   private(set) var gold: Int   private(set) var wood: Int   init(gold: Int, wood: Int) {       self.gold = gold       self.wood = wood   }}

另一种实现方法 — 可变函数

另一种我们可以解决上面的 Resources 问题的方法是使用可变函数而不是操作符重载。我们可以添加一个函数,通过另一个实例减少 Resources 值的属性,如下所示:

extension Resources {   mutating func reduce(by resources: Resources) {       gold -= resources.gold       wood -= resources.wood   }}

这两个解决方案都有它们的优点,您可以争辩说可变函数方法更明确。但是,您也不希望数学的标准减法api变成:5.reduce(by: 3),所以也许这是一个运算符重载表现完美的地方。

布局计算

让我们来看看另一种方案,其中使用操作符重载可能非常好。尽管我们拥有自动布局和强大的布局API,但有时我们发现自己在某些情况下需要进行手动布局计算。

在这样的情况下,它非常常见,必须在二维值上进行数学操作 —— 如 CGPoint,CGSize 和 CGVector。例如,我们可能需要通过使用图像视图的大小和一些额外的边距来计算标签的原点,如下所示:

label.frame.origin = CGPoint(   x: imageView.bounds.width + 10,   y: imageView.bounds.height + 20)

如果我们可以简单地添加它们,而不是必须始终展开 point 和 size 来使用他们的底层组件,这会不会很好(就像上面对 Resources 的操作一样)?

为了能够这样做,我们可以通过重载+运算符来接受两个 CGSize 实例作为输入,并输出 CGPoint 值:

extension CGSize {   static func +(lhs: CGSize, rhs: CGSize) -> CGPoint {       return CGPoint(           x: lhs.width + rhs.width,           y: lhs.height + rhs.height       )   }}

通过上面的代码,我们现在可以写下我们的布局计算:

label.frame.origin = imageView.bounds.size + CGSize(width: 10, height: 20)

这很酷,但必须为我们的位置创造 CGSize 会感到有点奇怪。使这个有点更好的一种方法可以是定义另一个 + 重载,该 + 重载接受包含两个 CGFloat 值的元组,如下所示:

extension CGSize {   static func +(lhs: CGSize, rhs: (x: CGFloat, y: CGFloat)) -> CGPoint {       return CGPoint(           x: lhs.width + rhs.x,           y: lhs.height + rhs.y       )   }}

这让我们在这两种方式中的任何一个写下我们的布局计算:

// 使用元组标签:label.frame.origin = imageView.bounds.size + (x: 10, y: 20)// 或者不写:label.frame.origin = imageView.bounds.size + (10, 20)

那非常紧凑,很好!但现在我们正在接近导致操作符的争论出现的核心问题 —— 平衡冗余程度和可读性。由于我们仍然处理数字,我认为大多数人会发现上面的易于阅读和理解,但随着我们继续自定义操作符的用途,它变得更加复杂,特别是当我们开始引入全新的操作符时。

处理错误的自定义运算符

到目前为止,我们还只是简单的重载了系统已经存在的操作符。但是,如果我们想开始使用无法真正映射到现有的功能的操作符,我们需要定义自己的。

让我们来看看另一个例子。Swift 的 do,try,catch 错误处理机制在处理无法使用的同步操作时超级漂亮。它可以让我们在出现错误后,轻松安全地退出函数。例如在加载磁盘上保存的数据模型时:

class NoteManager {   func loadNote(fromFileNamed fileName: String) throws -> Note {       let file = try fileLoader.loadFile(named: fileName)       let data = try file.read()       let note = try Note(data: data)       return note   }}

做出像上面的唯一主要的缺点是我们直接向我们功能的调用者抛出出任何潜在的错误,需要减少 API 可以抛出的错误量,否则做有意义的错误处理和测试变得非常困难。

理想情况下,我们想要的是给定 API 可以抛出的有限错误,这样我们就可以轻松地单独处理每种情况。让我们说我们也想捕捉所有潜在的错误,让我们同时拥有所有好的事情。因此,我们使用显式 cases 定义一个错误枚举,每个错误的枚举都使用底层错误的关联值,如下所示:

extension NoteManager {   enum LoadingError: Error {       case invalidFile(Error)       case invalidData(Error)       case decodingFailed(Error)   }}

但是,捕获潜在的错误并将它们转换为自己类型是棘手的。我们必须写下类似的标准错误处理机制:

class NoteManager {   func loadNote(fromFileNamed fileName: String) throws -> Note {       do {           let file = try fileLoader.loadFile(named: fileName)           do {               let data = try file.read()               do {                   return try Note(data: data)               } catch {                   throw LoadingError.decodingFailed(error)               }           } catch {               throw LoadingError.invalidData(error)           }       } catch {           throw LoadingError.invalidFile(error)       }   }}

我不认为有人想要阅读像上面的代码。一个选项是介绍一个 perfORM 函数,我们可以用来把一个错误转换为另一个错误:

class NoteManager {   func loadNote(fromFileNamed fileName: String) throws -> Note {       let file = try perform(fileLoader.loadFile(named: fileName),                              orThrow: LoadingError.invalidFile)       let data = try perform(file.read(),                              orThrow: LoadingError.invalidData)       let note = try perform(Note(data: data),                              orThrow: LoadingError.decodingFailed)       return note   }}func perform(_ expression: @autoclosure () throws -> T,               errorTransform: (Error) -> Error) throws -> T {   do {       return try expression()   } catch {       throw errorTransform(error)   }}

更好一点了,但我们仍然有很多错误转换代码会对我们的实际逻辑造成混乱。让我们看看引入新的操作符是否可以帮助我们清理此代码。

添加新的操作符

我们首先定义我们的新运营商。在这种情况下,我们将选择 〜> 作为符号(具有替代返回类型的动机,所以我们正在寻找类似于 ->)的东西。由于这是一个将在两侧工作操作符,因此我们将其定义为 infix,如下所示:

infix operator ~>

使操作符如此强大的是它们可以自动捕捉它们两侧的上下文。将其与Swift 的 @autoclosure 功能相结合,我们可以创建一些非常酷的东西。

让我们实现 〜> 作为传递表达式和转换错误的操作符,抛出或返回与原始表达式相同的类型:

func ~>(expression: @autoclosure () throws -> T,          errorTransform: (Error) -> Error) throws -> T {   do {       return try expression()   } catch {       throw errorTransform(error)   }}

那么上述这个操作符能够让我们做什么呢?由于枚举具有关联值的静态函数在Swift中也是静态函数,我们可以简单地在我们的抛出表达式和错误情况之间添加〜>操作符,我们希望将任何底层错误转换为如下形式:

class NoteManager {   func loadNote(fromFileNamed fileName: String) throws -> Note {       let file = try fileLoader.loadFile(named: fileName) ~> LoadingError.invalidFile       let data = try file.read() ~> LoadingError.invalidData       let note = try Note(data: data) ~> LoadingError.decodingFailed       return note   }}

这很酷!通过使用操作符,我们已从我们的逻辑中删除了大量的繁琐代码和语法,使我们的代码更为聚焦。然而,缺点是我们引入了一个新的错误处理语法,这可能是任何可能在未来加入我们项目的新开发人员完全不熟悉的。

结论

自定义操作符和操作符重载是一个非常强大的功能,可以让我们构建非常有趣的解决方案。它可以让我们降低呈现型函数调用的冗长,这可能会给我们清洁代码。然而,它也可以是一个滑坡,可以引导我们编写隐秘的和难以阅读的代码,这对其他开发人员来说变得非常令人恐惧和混淆。

就像以更高级的方式使用第一类函数时,我认为在引入新的运算符或创建额外的重载前,需要三思而后行。从其他开发人员获得反馈也可以超级有价值,作为一种新的操作符,对您的感觉和对别人的感觉完全不一样。与如此多的事情一样,理解权衡并试图为每种情况挑选最合适的工具

感谢你能够认真阅读完这篇文章,希望小编分享的“Swift语言中怎么自定义操作符”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网精选频道,更多相关知识等着你来学习!

--结束END--

本文标题: Swift语言中怎么自定义操作符

本文链接: https://www.lsjlt.com/news/314872.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Swift语言中怎么自定义操作符
    这篇文章主要介绍了Swift语言中怎么自定义操作符,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在Swift语言中,常见的操作符有+、-、*、/、>、、==、&...
    99+
    2023-06-28
  • 怎么在C语言中自定义类型
    本篇文章为大家展示了怎么在C语言中自定义类型,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、初始结构体结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。下面举一个例子:s...
    99+
    2023-06-08
  • C语言字符串数组怎么定义
    在C语言中,可以使用字符数组来表示字符串。字符串数组的定义方式有两种:1. 使用字符数组:可以通过声明一个字符数组来定义字符串数组。...
    99+
    2023-09-29
    C语言
  • springboot怎么自定义LocaleResolver切换语言
    这篇文章主要介绍了springboot怎么自定义LocaleResolver切换语言的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇springboot怎么自定义LocaleRe...
    99+
    2022-10-19
  • C语言中sizeof怎么在自定义函数中正常工作
    本文小编为大家详细介绍“C语言中sizeof怎么在自定义函数中正常工作”,内容详细,步骤清晰,细节处理妥当,希望这篇“C语言中sizeof怎么在自定义函数中正常工作”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。在...
    99+
    2023-06-30
  • ASP.NET中怎么创建自定义操作
    这期内容当中小编将会给大家带来有关ASP.NET中怎么创建自定义操作,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。ASP.NET安装部署之创建自定义操作1.在解决方案资源管理器中选择“Test Insta...
    99+
    2023-06-18
  • R语言—自定义函数求置信区间的操作
    看代码吧~ #求单正态均值mu的置信区间 #参数依次为置信水平alpha,正态样本x,已知总体方差(默认为未知) mu <- function(alpha,x,sigma=...
    99+
    2022-11-12
  • go语言预定义标识符怎么使用
    本文小编为大家详细介绍“go语言预定义标识符怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“go语言预定义标识符怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。go语言预定义标识符有36个,主要包含...
    99+
    2023-07-05
  • c语言怎么调用自定义函数
    要调用自定义函数,可以按照以下步骤进行:1. 在调用自定义函数之前,需要先进行函数的声明。函数声明一般写在程序的开头部分,表示该函数...
    99+
    2023-09-15
    c语言
  • C语言操作符++和--怎么使用
    本篇内容介绍了“C语言操作符++和--怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、++与--操作符的本质++ 和 -- 操作符...
    99+
    2023-06-30
  • R语言ggplot2x轴顺序设置自定义颜色的操作
    先声明一下所用的数据集 第一个图如下 这个图主要在于x轴的顺序设置上,如果按不做任何处理的话>3那个就会在2之前,解决方法是b[,1]<-factor(b[,1],l...
    99+
    2022-11-12
  • Go语言中转义符怎么使用
    这篇文章主要讲解了“Go语言中转义符怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Go语言中转义符怎么使用”吧!在Go语言中,转义字符是一种特殊的字符常量,以反斜线"\&q...
    99+
    2023-07-05
  • C语言中的自定义类型是什么
    C语言中的自定义类型是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。结构大小我们先随便给出一个结构体,为了计算他的大小,我给出完整的打印方案:typedef st...
    99+
    2023-06-28
  • 怎么在C语言中自定义结构体和枚举
    这篇文章将为大家详细讲解有关怎么在C语言中自定义结构体和枚举,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。C语言是什么C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发,使...
    99+
    2023-06-14
  • SQL语句中的DDL类型的数据库定义语言操作
    目录SQL语句之DDL类型的数据库定义语言1.DDL类型的SQL语句基本概述2.DDL类型的SQL语句之数据库层面的操作2.1.创建一个数据库2.2.查看mysql中有哪些数据库2.3.进入某个数据库2.4.查看当前处于...
    99+
    2022-08-09
    SQL DDL数据库定义语言 SQL数据库定义语言
  • c语言中怎么定义bit函数
    在C语言中,可以使用位域(bit-field)来定义一个函数。位域是一种特殊的结构体成员,可以指定成员占用的位数。 下面是一个例子,...
    99+
    2023-10-23
    c语言
  • PHP怎么实现词法分析与自定义语言
    本文小编为大家详细介绍“PHP怎么实现词法分析与自定义语言”,内容详细,步骤清晰,细节处理妥当,希望这篇“PHP怎么实现词法分析与自定义语言”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。    ...
    99+
    2023-06-26
  • MySQL怎么自定义变量和语句结束分隔符
    今天小编给大家分享一下MySQL怎么自定义变量和语句结束分隔符的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们...
    99+
    2022-10-19
  • C语言中自定义函数的流程是什么
    在C语言中,自定义函数的流程如下:1. 函数声明:在函数调用之前,需要先声明函数。函数声明包括函数的返回类型、函数名、参数的类型和个...
    99+
    2023-09-15
    C语言
  • Python语言怎么在C语言中实现操作
    这篇文章给大家介绍Python语言怎么在C语言中实现操作,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Python语言会在很多的语言中出现。我们在不断的学习和使用中存在着不少问题,下面我们就详细的来学习相关的知识以及如...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作