Переменная экземпляра: self vs @


179

Вот некоторый код:

class Person
  def initialize(age)
    @age = age
  end

  def age
    @age
  end

  def age_difference_with(other_person)
    (self.age - other_person.age).abs
  end

  protected :age
end

То, что я хочу знать, это разница между использованием @ageи self.ageв age_difference_withметоде.

Ответы:


260

Запись @ageнапрямую обращается к переменной экземпляра @age. Письмо self.ageговорит объекту отправлять себе сообщение age, которое обычно возвращает переменную экземпляра, @ageно может делать множество других вещей в зависимости от того, как ageметод реализован в данном подклассе. Например, у вас может быть класс MiddleAgedSocialite, который всегда сообщает о своем возрасте на 10 лет моложе, чем на самом деле. Или, на практике, класс PersistentPerson может лениво читать эти данные из постоянного хранилища, кэшировать все свои постоянные данные в хэш.


2
Однажды я прочитал книгу в рельсах и не понимаю разницы между этим self и @, поэтому я всегда должен использовать self.var_name в своих методах (которые не устанавливают и не получают) для создания моих данных с использованием открытого интерфейса. потратил время на определение его в методах получения и установки, верно?
Сарунв

1
... английский ... что вы подразумеваете под любым количеством вещей. я не получил эти последние два примера.
user2167582

23

Разница в том, что он изолирует использование метода от его реализации. Если реализация свойства должна была измениться - скажем, чтобы сохранить дату рождения, а затем вычислить возраст на основе разницы во времени между текущим временем и датой рождения - тогда код, зависящий от метода, менять не нужно. Если бы оно использовало свойство напрямую, то изменение должно было бы распространиться на другие области кода. В этом смысле непосредственное использование свойства более хрупко, чем использование предоставленного ему класса интерфейса.


15
Оооо, потому что self.age может ссылаться на переменную экземпляра или метод экземпляра?
Нолан Эми

@. @ ... это так печально
cyc115

7

Будьте осторожны, когда вы унаследуете класс, из Struct.newкоторого вы найдете отличный способ создания инициализатора ( Как создать инициализатор в Ruby? )

class Node < Struct.new(:value)
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value # or `p value`
    end
end 

n = Node.new(30)
n.show()

вернется

30
nil

Однако при удалении инициализатора он вернется

nil
30

С определением класса

class Node2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value
    end
end

Вы должны предоставить конструктор.

n2 = Node2.new(30)
n2.show()

вернется

30
30

Спасибо за пример @Prosseek, в настоящее время я изучаю Ruby on Rails, и именно это поведение заставляет меня чувствовать, что Ruby излишне сложен>. <.
cyc115

3

Первый ответ совершенно верен, но, как относительный новичок, я не сразу понял, что он имел в виду (отправка сообщений самому себе ... ага ...). Я думаю, что короткий пример поможет:

class CrazyAccessors
  def bar=(val)
    @bar = val - 20 # sets @bar to (input - 20)
  end
  def bar
    @bar
  end

  def baz=(value)
    self.bar = value # goes through `bar=` method, so @bar = (50 - 20)
  end

  def quux=(value)
    @bar = value     # sets @bar directly to 50
  end
end

obj  = CrazyAccessors.new
obj.baz = 50
obj.bar  # => 30
obj.quux = 50
obj.bar  # => 50

8
Этот пример сделал вещи более запутанными.
Оскар Холмкрац

1
Прошу прощения, но пример недостаточно прокомментирован для меня. Я не могу следовать вашим рассуждениям.
Коути

Кто-то, кто пришел из Smalltalk, скажет, что объект «отправляет сообщение самому себе». Кто-то, пришедший из Python, скажет, что объект «вызывает метод сам по себе». Не смущайтесь; это одно и то же. (Пуантист семантики может возразить, что они совпадают только для языков с динамической типизацией и что вызов виртуального метода C ++ не совсем то же самое, что отправка сообщения. Пурист верен, но это, вероятно, выходит за рамки этого вопроса / ответ.)
GrandOpener

Мне нравится пример, но, пожалуйста, предоставьте еще несколько комментариев о том, что на самом деле происходит. Трудно следовать без объяснения причин
CalamityAdam

2

Там нет никакой разницы. Я подозреваю, что это было сделано только для документальной ценности видения self.ageи other_person.ageрядом друг с другом.

Я полагаю, что использование позволяет в будущем написать реальный геттер, что может сделать что-то более сложное, чем просто вернуть переменную экземпляра, и в этом случае метод не нужно будет менять.

Но об этой абстракции вряд ли стоит беспокоиться, в конце концов, если реализация объекта изменилась, разумно изменить другие методы, в какой-то момент простая ссылка в самом объекте совершенно разумна.

В любом случае, абстракция ageсвойства все еще не объясняет явного использования self, поскольку просто ageбы также вызвал метод доступа.


-3

@age - определенно переменная age

self.age - относится к возрасту свойства экземпляра.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.