请原谅所有新手的问题,但为什么@game_score 总是为零?
#bowling.rb
class Bowling
@game_score = 0
def hit(pins)
@game_score = @game_score + pins
end
def score
@game_score
end
end
请原谅所有新手的问题,但为什么@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
中。记住:类和其他对象一样只是对象。因此,此时您正在分配给类 object0
的实例变量。@game_score
Bowling
def hit(pins)
@game_score = @game_score + pins # (2)
现在(2),我们在类的实例方法中Bowling
。即:这是一个将属于. Bowling
所以,现在实例变量@game_score
属于类的一个实例,Bowling
而不是属于类本身。
由于此实例变量从未初始化为任何内容,因此它将评估为nil
(在 Ruby 中,未初始化的变量始终评估为nil
),因此 this 评估为@game_score = nil + pins
并且由于nil
没有#+
方法,这将导致NoMethodError
引发异常。
end
def score
@game_score # (3)
在这里(3),我们再次在类的实例方法中Bowling
。nil
由于我上面概述的原因,这将始终评估为:@game_score
从未初始化,因此它评估为nil
。
end
end
我们可以使用 Ruby 的反射能力来看看是怎么回事:
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
顺便说一句:只是一些小的 Ruby 样式提示:缩进是 2 个空格,而不是 1 个制表符。你的hit
方法会更惯用@game_score += pins
.
因为你没有
def initialize
@game_score = 0
end
类定义中的赋值没有做你认为它正在做的事情,当hit
被调用时它不能添加到nil
.
如果你现在问怎么了@game_score
?,好吧,永远记住Class 是一个对象,而Object 是一个类。
Ruby 类拥有这种禅宗般的“真实”存在方式真是太酷了。Ruby 并没有精确地命名类,而是类名是对 class 对象的引用Class
。通过分配给@game_score
实例方法的外部,您创建了一个类实例变量,即类对象的一个属性Bowling
,它是类的一个实例Class
。这些对象通常不是很有用。(参见第 1 章,Ruby 方式,Hal Fulton。)
@game_score
在那里定义的称为类实例变量,它是为单例类对象定义的变量:
class << Bowling
attr_accessor :game_score
end
Bowling.game_score #=> 0
这与为实例对象定义的普通实例变量不同。
@game_score 在这里永远不会得到零值 - 你需要把它放在初始化中,如
def 初始化 @game_score = 0 结束