前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swift 5.2 将实例作为函数调用

Swift 5.2 将实例作为函数调用

作者头像
韦弦zhy
发布2020-03-19 20:44:41
2.4K0
发布2020-03-19 20:44:41
举报
文章被收录于专栏:韦弦的偶尔分享

Swift 5.2中的一个新功能是可以将类型实例作为函数调用(callAsFunction)。或者,如Swift Evolution 提案所述,“用户定义的标称类型的可调用值”。此函数的简短描述是,它允许您调用实现了callAsFunction方法的任何类型的实例,就好像它是一个函数一样

callAsFunction

例如一个计算每年财富值以8%递增的计算器,传递一个初始值以及经过多少年,得出最终的财富:

代码语言:javascript
复制
struct InvestmentsCalculator {
  let input: Double
  let averageGrowthPerYear = 0.08

  func callAsFunction(years: Int) -> Double {
    return (0..<years).reduce(input, { value, _ in
      return value * (1 + averageGrowthPerYear)
    })
  }
}

let calculator = InvestmentsCalculator(input: 1000)
let newValue = calculator(years: 10)

实现了callAsFunction方法后,可以直接将实例当做函数使用,他的具体实现就是callAsFunction的实现。

尽管这很酷,但您可能更想知道这样的功能在实际编程过程中何时有用,以及如何在代码中应用它。

Swift不是唯一允许其用户调用某些类型的实例作为函数的语言,比如:

它是 @dynamicCallable 的静态形式

SE-0216引入了用户定义的动态可调用值。在考虑的可替代方案部分中,要求我们与提议的动态版本一起设计和实现该提议的“静态可调用”版本。有关“静态可调用项”的讨论,请参照pitch thread

在Swift中使用callAsFunction相对简单。任何定义callAsFunction方法的对象都可以视为一个函数。您的callAsFunction可以接受参数并返回值,如Swift Evolution建议中所示,并带有以下示例:

代码语言:javascript
复制
struct Adder {
  let base: Int

  func callAsFunction(_ x: Int) -> Int {
    return base + x
  }
}

let add3 = Adder(base: 3)
add3(10) // 13

您甚至可以在一个对象上有多个重载:

代码语言:javascript
复制
struct Adder {
    var base: Int

    func callAsFunction(_ x: Int) -> Int {
        return base + x
    }

    func callAsFunction(_ x: Float) -> Float {
        return Float(base) + x
    }

    func callAsFunction<T>(_ x: T, bang: Bool) throws -> T where T: BinaryInteger {
        if bang {
            return T(Int(exactly: x)! + base)
        } else {
            return T(Int(truncatingIfNeeded: x) + base)
        }
    }
}

let add1 = Adder(base: 1)
add1(2) // => 3
try add1(4, bang: true) // => 5

当类型检查失败时,错误消息看起来类似于函数调用的错误消息。如有歧义,编译器将显示相关的callAsFunction方法候选对象。

代码语言:javascript
复制
add1("foo")
// error: cannot invoke 'add1' with an argument list of type '(String)'
// note: overloads for functions named 'callAsFunction' exist with these partially matching parameter lists: (Float), (Int)
add1(1, 2, 3)
// error: cannot invoke 'add1' with an argument list of type '(Int, Int, Int)'
直接引用callAsFunction

由于callAsFunction方法是一种普通方法,因此可以使用其声明名称引用callAsFunction方法,并获取捕获self的闭包。这正是今天方法引用的工作方式。

代码语言:javascript
复制
let add1 = Adder(base: 1)
let f1: (Int) -> Int = add1.callAsFunction
let f2: (Float) -> Float = add1.callAsFunction(_:)
let f3: (Int, Bool) throws -> Int = add1.callAsFunction(_:bang:)

当类型也是@dynamicCallable时

类型既可以具有callAsFunction方法,也可以使用@dynamicCallable声明。在对调用表达式进行类型检查时,类型检查器将首先尝试将调用解析为函数或初始化程序调用,然后将其解析为callAsFunction方法调用,最后是动态调用。

能够决定是否要让callAsFunction实现接受参数以及返回类型是什么的能力使其成为一个非常强大的功能。您确实可以根据自己的需求自定义此功能,并且由于可以向对象添加多个callAsFunction重载,因此可以在多个上下文中将单个对象用作函数。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 它是 @dynamicCallable 的静态形式
  • 直接引用callAsFunction
  • 当类型也是@dynamicCallable时
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档