13

在 Ruby 1.8.7 和 1.9.2 中相同:

$ irb

ruby-1.8.7-p302 > foo.nil?
NameError: undefined local variable or method `foo' for #<Object:0x3794c>
    from (irb):1

ruby-1.8.7-p302 > @bar.nil?
 => true 

ruby-1.8.7-p302 > @@wah.nil?
NameError: uninitialized class variable @@wah in Object
    from (irb):3

为什么实例变量的处理方式与局部变量和类变量不同?

4

1 回答 1

17

在 Ruby 中,大多数未初始化甚至不存在的变量的计算结果为nil. 这适用于局部变量、实例变量和全局变量:

defined? foo       #=> nil
local_variables    #=> []
if false
  foo = 42
end
defined? foo       #=> 'local-variable'
local_variables    #=> [:foo]
foo                #=> nil
foo.nil?           #=> true

defined? @bar      #=> nil
instance_variables #=> []
@bar               #=> nil
@bar.nil?          #=> true
# warning: instance variable @bar not initialized

defined? $baz      #=> nil
$baz               #=> nil
# warning: global variable `$baz' not initialized
$baz.nil?          #=> true
# warning: global variable `$baz' not initialized

但是,对于类层次结构变量和常量,情况并非如此:

defined? @@wah     #=> nil
@@wah
# NameError: uninitialized class variable @@wah in Object

defined? QUUX      #=> nil
QUUX
# NameError: uninitialized constant Object::QUUX

这是一条红鲱鱼:

defined? fnord     #=> nil
local_variables    #=> []
fnord
# NameError: undefined local variable or method `fnord' for main:Object

在这里出现错误的原因不是未初始化的局部变量不计算为nil,而是fnord模棱两可:它可能是发送到默认接收器的无参数消息(即等效于self.fnord()访问局部变量fnord

为了消除歧义,您需要添加一个接收者或一个参数列表(即使是空的)来告诉 Ruby 这是一个消息发送:

self.fnord
# NoMethodError: undefined method `fnord' for main:Object
fnord()
# NoMethodError: undefined method `fnord' for main:Object

或者确保解析器而不是评估器)在使用之前解析(执行)赋值,告诉 Ruby 它是一个局部变量:

if false
  fnord = 42
end
fnord              #=> nil

为什么实例变量的处理方式与局部变量和类变量不同?

其实不是。它被视为与局部变量相同。类层次结构变量的行为不同,局部变量、实例变量和全局变量的行为都相同。

还有其他原因……类变量也不能像那样表现吗?

我不知道。对于实例变量,它非常方便,因为与 Java 不同,例如,实例变量在类定义中声明,因此类的每个实例始终存在,在 Ruby 中,实例变量不在任何地方声明。一旦被分配,它们就会神奇地出现。由于不一定保证实例变量存在,因此编写使用实例变量的方法如果抛出异常会很痛苦。

为什么类层次结构变量不同,我不知道。也许是因为无论如何都没有人使用它们,或者因为它们通常倾向于在类主体中初始化,并且在未初始化时根本无法访问。

于 2010-10-26T11:37:00.080 回答