新手入门: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
Pardon the total newbiew question but why is @game_score always nil?
#bowling.rb
class Bowling
@game_score = 0
def hit(pins)
@game_score = @game_score + pins
end
def score
@game_score
end
end
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
让我们看一下代码,好吗?
此时 (1),我们仍然位于 class
Bowling
中。请记住:类与其他对象一样只是对象。因此,此时您将0
分配给类对象Bowling
的实例变量@game_score
。现在 (2),我们位于
Bowling
类的实例方法中。即:这是一个属于Bowling
实例的方法。因此,现在实例变量@game_score
属于Bowling
类的实例,而不是该类本身。由于 this 实例变量从未初始化为任何内容,因此它将计算为 nil(在 Ruby 中,未初始化的变量始终计算为 nil),因此计算结果为
@game_score = nil + pin
并且由于nil
没有#+
方法,这将导致NoMethodError
引发异常。在这里 (3),我们再次位于
Bowling
类的实例方法中。由于我上面概述的原因,它的计算结果始终为nil
:@game_score
从未初始化,因此它的计算结果为nil
。我们可以使用 Ruby 的反射功能来看看发生了什么:
现在让我们向实例变量注入一个值:
所以,我们看到一切都按预期进行,我们只需要弄清楚如何确保实例变量得到已初始化。
为此,我们需要编写一个初始化方法。奇怪的是,初始化方法实际上是一个名为
initialize
的私有实例方法。 (initialize
是实例方法而不是类方法的原因其实很简单。Ruby 将对象创建分为两个阶段:内存分配和对象初始化。内存分配是由 完成的类方法称为alloc
,而对象初始化是由实例称为initialize
的方法完成的(Objective-C程序员会认识到这一点。 )alloc 是类方法的原因很简单,在执行过程中还没有实例,而initialize 是实例方法的原因是该对象。初始化显然是针对每个对象的。为了方便起见,有一个名为new
的标准工厂类方法,它可以为您调用alloc
和initialize
。 )让我们测试一下:
顺便说一句:只是一些小的 Ruby 风格提示:缩进是 2 个空格,而不是 1 个制表符。而你的
hit
方法更惯用的是@game_score += pin
。Let's walk through the code, shall we?
At this point (1), we are still inside the class
Bowling
. Remember: classes are just objects like any other. So, at this point you are assigning0
to the instance variable@game_score
of the class objectBowling
.Now (2), we are inside an instance method of the
Bowling
class. I.e.: this is a method that is going to belong to an instance ofBowling
. So, now the instance variable@game_score
belongs to an instance of theBowling
class, and not to the class itself.Since this instance variable is never initialized to anything, it will evaluate to
nil
(in Ruby, uninitialized variables always evaluate tonil
), so this evaluates to@game_score = nil + pins
and sincenil
doesn't have a#+
method, this will result in aNoMethodError
exception being raised.And here (3), we are again inside an instance method of the
Bowling
class. This will always evaluate tonil
, for the reason I outlined above:@game_score
is never initialized, therefore it evaluates tonil
.We can use Ruby's reflection capabilities to take a look at what's going on:
Now let's inject a value into the instance variable:
So, we see that everything works as it should, we only need to figure out how to make sure the instance variable gets initialized.
To do that, we need to write an initializer method. Strangely, the initializer method is actually a private instance method called
initialize
. (The reason whyinitialize
is an instance method and not a class method, is actually quite simple. Ruby splits object creation in two phases: memory allocation and object initialization. Memory allocation is done by a class method calledalloc
and object initialization is done by an instance method calledinitialize
. (Objective-C programmers will recognize this.) The reason whyalloc
is a class method is simply that at this point in the execution there is no instance yet. And the reason thatinitialize
is an instance method is that object initialization is obviously per-object. As a convenience, there is a standard factory class method callednew
that calls bothalloc
andinitialize
for you.)Let's test this:
BTW: just some minor Ruby style tips: indentation is 2 spaces, not 1 tab. And your
hit
method would more idiomatically be@game_score += pins
.因为您没有
类定义中的赋值没有执行您认为它正在执行的操作,并且当调用
hit
时,它无法添加到nil
。如果您现在问
@game_score
发生了什么?,那么,请永远记住类是一个对象而对象是一个类。Ruby 类拥有这种禅宗般的“真实”存在,这真是太酷了。 Ruby 并不精确地具有命名类,相反,类名是对类
Class
的对象的引用。通过在实例方法之外分配给@game_score
,您创建了一个类实例变量,它是类对象Bowling
的一个属性,它是一个实例类Class
。一般来说,这些对象不是很有用。 (请参阅第 1 章,The Ruby Way,Hal Fulton。)Because you don't have
The assignment in the class definition is not doing what you think it is doing, and when
hit
gets invoked it can't add tonil
.If you now ask what happened to
@game_score
?, well, always remember Class is an object and Object is a class.It's way cool the way Ruby classes have this Zen-like "real" existence. Ruby doesn't precisely have named classes, rather, class names are references to objects of class
Class
. By assigning to@game_score
outside of an instance method you created a class instance variable, an attribute of the class objectBowling
, which is an instance of classClass
. These objects are not, in general, very useful. (See Chapter 1, The Ruby Way, Hal Fulton.)这里定义的@game_score被称为类实例变量,它是为单例类对象定义的变量:
这与普通的实例变量不同 为实例对象定义。
@game_score
defined there is called class instance variable, which is a variable defined for the singleton class object:This is as you can tell different from the normal instance variables defined for instance objects.
@game_score 在这里永远不会得到零值 - 你需要将它放在初始化中,如
def 初始化
@游戏分数 = 0
结尾
@game_score will never get a value of zero here - you need to put it inside initialize, as in
def initialize
@game_score = 0
end