实例变量: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

我想知道的是in @ageself.agein age_difference_with方法之间的区别。

Answers:


260

@age直接写访问实例变量@age。编写过程self.age告诉对象向自身发送消息age,该消息通常将返回实例变量@age-但可以执行任何其他操作,具体取决于在age给定子类中方法的实现方式。例如,您可能有一个MiddleAgedSocialite类,该类总是报告其年龄比实际年龄小10岁。或更实际地说,PersistentPerson类可能会从持久性存储中延迟读取该数据,并将其所有持久性数据缓存在哈希中。


2
我曾经在Rails上读过一本书,但不了解此self和@之间的区别,所以我应该始终在我的方法中使用self.var_name(不使用setter和getter),以使用公共接口来创建数据,我花了一些时间在getter和setter中定义它,对吗?
sarunw

1
...英语...您所说的任何事物是什么意思。我没有得到最后两个例子。
user2167582 2014年

23

不同之处在于,它将方法的使用与方法的实现隔离开来。如果要更改属性的实现(例如保留出生日期,然后根据现在和出生日期之间的时间差来计算年龄),则无需更改取决于方法的代码。如果它直接使用该属性,则更改将需要传播到代码的其他区域。从这个意义上讲,直接使用属性比使用类提供的接口更脆弱。


15
哦,因为self.age可以引用实例变量还是实例方法?
Nolan Amy

@。@ ...可悲的是
cyc115 '18

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 '18

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
这个例子使事情变得更加混乱。
Oskar Holmkratz '16

1
很抱歉,这个例子对我来说还不够。我听不懂你的推理。
kouty

来自Smalltalk的人会说对象“向自身发送消息”。某些来自Python的人会说一个对象“对其自身调用方法”。不要感到困惑;他们是完全一样的东西。(语义纯粹主义者可能会反对说,它们仅与具有动态类型的语言相同,并且C ++虚拟方法调用与发送消息并不完全相同。纯粹主义者是正确的,但这可能超出了本问题的范围/答案。)
GrandOpener

我喜欢该示例,但请提供一些有关实际情况的更多评论。很难跟随,没有解释
CalamityAdam

2

没有任何区别。我怀疑这样做只是出于见面self.ageother_person.age彼此接近的记录价值。

我认为使用确实允许将来编写实际的getter,这可能会做得比返回一个实例变量还要复杂,在这种情况下,方法不需要更改。

毕竟,如果要更改对象的实现更改其他方法是合理的,那么担心这是不太可能的抽象,在某个时候,对象本身内部的简单引用是完全合理的。

在任何情况下,age属性的抽象仍然不能解释的显式使用self,因为普通age也会调用访问器。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.