我在Swift 4中有以下课程:
class AClass {
var add: (Int) -> Int {
return {
num in
return num + 1
}
}
func add(_ num: Int) -> Int {
return num + 20
}
}
请注意,变量和函数具有相同的名称“ add”。
现在在其他地方,我有以下代码:
let a = AClass()
print(a.add(1))
我已经运行了这段代码,结果为2(这意味着调用了该变量的块)。
所以这是问题:
编译器是否总是获取变量而不是调用函数?
有什么方法可以调用该函数?
答案 0 :(得分:2)
在Swift中,instance methods are curried functions。这意味着您的add
方法实际上是
static func add(_ self: AClass) -> (Int) -> Int
这就是为什么要在函数中选择属性的原因。调用AClass.add(a)(1)
会产生预期的结果,该函数将被调用。
现在,为什么编译器为什么允许此名称首位冲突?我不确定,但我认为这与这两个实体的完全限定名称有关。该属性仅为add
,而函数为add(_:)
。
答案 1 :(得分:2)
虽然属性和方法共享相同的基本名称add
,但它们具有不同的完整名称。该方法的全名是add(_:)
,因为它具有一个参数(没有参数标签),而属性的全名只是add
。它们的全名不同的事实是使它们彼此过载的原因。
如果该方法没有参数,则编译器将不允许重载,因为它们的全名现在都是add
并因此发生冲突:
class AClass {
var add: () -> Int {
return {
return 1
}
}
func add() -> Int { // error: Invalid redeclaration of 'add()'
return 2
}
}
编译器是否总是获取变量而不是调用函数?
假设它们具有相同的功能类型(例如您的示例),则为是。有一个重载排序规则,它优先考虑变量而不是函数:
// If the members agree on instance-ness, a property is better than a // method (because a method is usually immediately invoked). if (!decl1->isInstanceMember() && decl2->isInstanceMember()) score1 += weight; else if (!decl2->isInstanceMember() && decl1->isInstanceMember()) score2 += weight; else if (isa<VarDecl>(decl1) && isa<FuncDecl>(decl2)) score1 += weight; else if (isa<VarDecl>(decl2) && isa<FuncDecl>(decl1)) score2 += weight;
为了调用该方法,您可以使用其全名引用它,例如:
let a = AClass()
print(a.add(_:)(1)) // 21
答案 2 :(得分:0)
将方法的定义更改为此:
func add(num: Int) -> Int {
return num + 20
}
通知,我删除了_
这将迫使您以这种方式add(num:)
来调用方法,因此,您将能够区分方法调用和变量初始化。
对于您的问题1.)您实际上声称是变量的实际上是闭包。
答案 3 :(得分:0)
您对Swift的解析行为提出了一个非常有趣的问题。我相信正确的答案是这是不确定的行为。 add(1)
意味着应用存储在变量add
中的闭包或执行函数add
。两者具有相同的签名,并且解析器/运行时库只是选择第一个解释。我在这里找不到有关预期行为的任何文档,因此将其放在 undefined 下。
无论是否定义,我们都可以同意这是一个不好的命名方案。最好避免故意依赖编译器细微之处的程序。若要将两者分开,请删除函数中的下划线:
class AClass {
var add: (Int) -> Int {
return {
num in
return num + 1
}
}
func add(num: Int) -> Int {
return num + 20
}
}
let cls = AClass()
print(cls.add(1)) // 1
print(cls.add(num: 1)) // 21
答案 4 :(得分:0)
那里我们不能执行具有相同名称的变量和函数或更紧密的函数。
我们将需要删除_
符号。使编译器可以执行通过函数参数的外部名称区分的变量或函数。因为下划线_
用于使第二个或后续参数的外部名称在调用方法时不出现。
您将需要按以下方式更改方法的定义
func add(num: Int) -> Int {
return num + 20
}
使用
要执行变量:print(a.add(2))
要执行功能:print(a.add(num: 2))
但是,当您调用如下所示的函数/方法时,我们可以通过命名来设置它们要传递的参数。
func add(value num: Int) -> Int {
return num + 20
}
使用
要执行功能:print(a.add(value: 2))
这样我们就可以根据需要获得结果。
您可以访问以下链接以了解Swift语言中下划线的行为。
https://dmitripavlutin.com/useful-details-about-underscore-keyword-in-swift/
答案 5 :(得分:0)
另一种方法是
在这种情况下,您可以通过全称来引用该函数...
let add = a.add(_:)
print(add(1))
...但这不是一个很好的一般答案。