新手总数:ruby中的实例变量?
请原谅新的总体问题,但为什么@game_score总是为零呢?
#bowling.rb class Bowling @game_score = 0 def hit(pins) @game_score = @game_score + pins end def score @game_score end end
让我们来看看代码,好吗?
#bowling.rb class Bowling @game_score = 0 # (1)
在这一点上(1),我们仍然在Bowling
class内。 记住:类就像其他任何对象一样。 因此,此时您将0
分配给类对象Bowling
的实例变量@game_score
。
def hit(pins) @game_score = @game_score + pins # (2)
现在(2),我们在Bowling
类的实例方法中。 即:这是一种属于Bowling
实例的方法。 所以,现在实例变量@game_score
属于Bowling
类的一个实例 , @game_score
属于类本身。
由于这个实例变量永远不会被初始化为任何东西,它将评估为nil
(在Ruby中,未初始化的变量总是计算为nil
),因此这将评估为@game_score = nil + pins
,因为nil
没有#+
方法,将导致引发NoMethodError
exception。
end def score @game_score # (3)
在这里(3),我们再次在Bowling
类的实例方法中。 这总是评估为nil
,因为我上面概述的原因: @game_score
永远不会被初始化,因此它的计算结果为nil
。
end end
我们可以使用Ruby的reflectionfunction来查看正在发生的事情:
p Bowling.instance_variable_get(:@game_score) # => 0 b = Bowling.new p b.instance_variable_get(:@game_score) # => nil
现在让我们在实例变量中注入一个值:
b.instance_variable_set(:@game_score, 1) p b.score # => 1 b.hit(3) p b.score # => 4
所以,我们看到一切都按预期工作,我们只需要弄清楚如何确保实例变量被初始化。
为此,我们需要编写初始化方法。 奇怪的是,初始化方法实际上是一个名为initialize
的私有实例方法 。 ( initialize
是实例方法而不是类方法的原因实际上非常简单.Ruby分两个阶段创建对象:内存分配和对象初始化。内存分配由一个名为alloc
的类方法完成,对象初始化由一个名为initialize
的实例方法。(Objective-C程序员会认识到这一点。) alloc
是一个类方法的原因很简单,就是在执行的这一点上还没有实例。而initialize
是一个实例方法的原因是对象初始化显然是每个对象。为方便起见,有一个名为new
的标准工厂类方法,它为你调用alloc
和initialize
。)
class Bowling def initialize @game_score = 0 end end
我们来测试一下:
c = Bowling.new p c.score # => 0 c.hit(2) p c.score # => 2
BTW:只是一些小的Ruby风格提示:缩进是2个空格,而不是1个标签。 而你的hit
方法更像是@game_score += pins
。
因为你没有
def initialize @game_score = 0 end
类定义中的赋值没有按照您的想法执行,并且当调用hit
时,它不能添加到nil
。
如果你现在问@game_score
发生了@game_score
? 好吧,永远记得Class是一个对象而Object是一个类 。
Ruby类具有这种类似Zen的“真实”存在的方式很酷。 Ruby并不精确地具有命名类,而是类名是对类Class
对象的引用。 通过在实例方法之外分配@game_score
,您创建了一个类实例变量 ,类对象Bowling
一个属性,它是类Class
一个实例。 通常,这些对象非常有用。 (参见第1章, Ruby Way ,Hal Fulton。)
@game_score
定义了一个名为类的实例变量 ,它是为单例类对象定义的变量:
class << Bowling attr_accessor :game_score end Bowling.game_score #=> 0
这可以与为实例对象定义的普通实例变量区别开来。
@game_score永远不会在这里得到零值 – 你需要把它放在初始化中,如同
def初始化@game_score = 0结束