类具有与函数名称相同的变量名

时间:2019-02-13 04:20:03

标签: swift

我在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(这意味着调用了该变量的块)。

所以这是问题:

  1. 编译器是否总是获取变量而不是调用函数?

  2. 有什么方法可以调用该函数?

6 个答案:

答案 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;
     

lib/Sema/CSRanking.cpp

为了调用该方法,您可以使用其全名引用它,例如:

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))

...但这不是一个很好的一般答案。