Класс имеет имя переменной, совпадающее с именем функции - PullRequest
0 голосов
/ 13 февраля 2019

У меня есть следующий класс в 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 голосов
/ 14 февраля 2019

Другой способ -

Вы можете перейти к функции, указав ее полное имя в этом случае…

let add = a.add(_:)
print(add(1))

… но это не очень хороший общий ответ.

0 голосов
/ 13 февраля 2019

Хотя свойство и метод имеют одно и то же базовое имя 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
0 голосов
/ 13 февраля 2019

Там мы не можем выполнять переменную и функцию или ближе с тем же именем.

Нам нужно будет удалить знак _.Таким образом, этот компилятор может выполнять переменную или функцию дифференцировать по внешнему имени параметра функции.Поскольку подчеркивание _ используется для того, чтобы внешнее имя второго или последующего параметра не отображалось при вызове метода.

Вам потребуется изменить определение метода следующим образом

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/

0 голосов
/ 13 февраля 2019

В Swift методы экземпляра являются функциями карри .Это означает, что ваш метод add на самом деле

static func add(_ self: AClass) -> (Int) -> Int

Именно поэтому свойство выбирается над функцией.Вызов AClass.add(a)(1) дает ожидаемый результат, вызывается функция.

Теперь, почему компилятор допускает конфликт с этим именем, если он стоит на первом месте?Я не уверен, но я предполагаю, что это связано с полностью определенным именем этих двух объектов.Свойство просто add, а функция add(_:).

0 голосов
/ 13 февраля 2019

Вы поднимаете очень интересный вопрос о поведении Свифта при разборе.Я считаю, что правильный ответ это неопределенное поведение .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
0 голосов
/ 13 февраля 2019

измените определение вашего метода следующим образом:

func add(num: Int) -> Int {
    return num + 20
}

обратите внимание, я удалил _, это заставит вас вызывать метод следующим образом add(num:), таким образом, вы сможете различитьмежду вызовом метода и инициализацией переменной.

для вашего вопроса 1.) то, что вы фактически называете переменной, на самом деле является замыканием.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...