Swift:消除作为类型柯里化(Currying)函数的方法之间的歧义(仅限 < Swift 2.2)

标签 swift protocols option-type

请注意,随着 Swift 2.2 的发布,这个问题已经得到解决——请参阅 The Swift Programming Language 一书的“Explicit Member Expression”部分。


在 swift 中,您可以对一个方法进行可选调用,该方法可能会或可能不会按照协议(protocol)实现:

@objc protocol F {
    optional func f(#p1: String, p2: String) -> String
}

@objc class C: F {
    func f(#p1: String, p2: String) -> String { return "0 \(p1) \(p2)" }
}

let c: F = C()

c.f?(p1: "1", p2: "2") // --> "0 1 2"

或者您也可以选择将方法绑定(bind)到本地常量:

if let f = c.f {
    f(p1: "1", p2: "2")
}

这是一种优雅的方式来包装一些代码,这些代码应该只在实现方法时发生,而不仅仅是调用本身。但是请注意,与 NSObjectProtocolrespondsToSelector: 不同,可选绑定(bind)不检查命名参数。因此,如果您有两个仅参数名称不同的方法,就会出现 Ambiguous use of 'f' 编译时错误:

@objc protocol F {
    optional func f(#p1: String, p2: String) -> String
    optional func f(#x: String, y: String) -> String
}

@objc class C: F {
    func f(#p1: String, p2: String) -> String { return "0 \(p1) \(p2)" }
    func f(#x: String, y: String) -> String { return "0 \(x) \(y)" }
}

let c: F = C()

c.f?(p1: "1", p2: "2") // --> "0 1 2"
c.f?(x: "1", y: "2") // --> "0 1 2"

if let f = c.f { // Ambiguous use of 'f'
    f(p1: "1", p2: "2")
}

这就是 Swift - Obj C 互操作性的问题。或者我错过了什么,实际上有一些可用的 Swift 语法可以消除这两种方法之间的歧义?或者我应该放弃设计使用可选方法的代码并简单地使用多个协议(protocol)来满足需求的多样性(这可能反过来用大量协议(protocol)名称填充类型定义)?

编辑

我最初的想法是按照下面 Sulthan 的回答。然而,我现在开始更倾向于他原来的评论:“这似乎是语法中的一个遗漏。我会报告它。”这样做的原因与将实例方法引用为该类型的柯里化(Currying)函数的模拟情况有关:

class C {
    func f() -> String { return "I'm a C" }
}

let c = C()
c.f() //--> "I'm a C"

let f = C.f(c)
f() //--> "I'm a C"

这很好,people are already making good use of this Swift 的基本功能。

当然你也可以对命名参数做同样的事情:

class D {
    func g(#x: Int) -> String { return "Gave me \(x)" }
    func g(#y: Int) -> String { return "Give me \(y)" }
}

let d = D()
d.g(x: 5) //--> "Gave me 5"
d.g(y: 15) //--> "Gave me 15"

D.g(d)(x: 5) //--> "Gave me 5"

再一次,与上面的可选方法一样,我不知道如何在没有立即调用的情况下区分“f”和“g”:

let g = D.g(d) // Ambiguous use of 'g'
g(x: 5)

请注意,类型注释会有所帮助(当然,假设类型被声明为不同的):

class D {
    func g(#x: Int) -> String { return "Gave me \(x)" }
    func g(#y: Double) -> String { return "Give me \(y)" }
}

let d = D()
let g: (x: Int) -> String = D.g(d)
g(x: 5) //--> "Gave me 5"

但是,编译器会忽略 (x: Int) 中的参数名称,如果 xy 中的引用仍然是不明确的是一个 Int...

类似的:

let p = (x: 0, y: 1)
p is (a: Int, b: Int) //--> true

虽然:

let q: (a: Int, b: Int) = (x: 0, y: 1) //--> Error: '(x: Int, y: Int)' is not convertible to '(a: Int, b: Int)`

最佳答案

我刚刚检查了 Swift 语法的相关部分,所以我确定语法中目前没有办法区分这两个函数。相关部分是

表达式

Implicit-member-expression → .­identifier

没有办法向标识符添加外部参数。

但是请注意,可选方法在纯 Swift 中并不存在,它们存在只是因为 Obj-C 有它们,所以 Swift 没有什么大的理由为可选方法提供特殊语法。另请注意,即使是 Obj-C 也没有用于检查可选方法是否存在的特殊语法。 Obj-C 使用反射 - 调用 respondsToSelector 将为您提供所需的信息。你可以在 Swift 中做同样的事情。参见 What is the swift equivalent of respondsToSelector?

关于Swift:消除作为类型柯里化(Currying)函数的方法之间的歧义(仅限 < Swift 2.2),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29698580/

相关文章:

java - 使用 Java 8 的 Monad

c++ - 使用什么 std::optional 或 std::unique_ptr

swift - 如何快速在 UITableView 上显示 UIActivityIndi​​cator

ios - SwiftUI Form 和 StackNavigationViewStyle 狂奔

ios - 面向协议(protocol)的编程示例在 swift 3.0 中不起作用

protocols - Protocol Buffer 日志记录

sockets - 将 "Large"数据文件传输到服务器的简单 TCP 协议(protocol)

ios - 有没有办法强制 Swift 中的 TextField 中的文本是非可选的?

arrays - 什么时候在 Swift 中对数组使用枚举?

ios - 如何在 swift 5 中从另一个 Controller 重新加载 UIViewController