很好的问题。不幸的是,您刚刚跳入了一个兔子洞,但您最终必须在 ruby 中跌入这个洞,才能开始了解真正的复杂性。
对于您的第一个问题,关于$
-prefixed 全局变量。它们是真正全球化的:
def mk_foo() $foo ||= "foo"; end
$foo # => nil
mk_foo # => "foo"
$foo # => "foo"
mk_foo.object_id # => 70299647799620
$foo.object_id # => 70299647799620
可以看到,when$foo
是在mk_foo
方法中定义的,是在全局空间中定义的,可以在任何地方访问:
class CanSeeFoo
def see_foo() $foo; end
end
CanSeeFoo.new.can_see_foo
# => "foo"
CanSeeFoo.new.can_see_foo.object_id
# => 70299647799620
至于类变量问题,这就是兔子洞的开始。首先,您是正确的,@@
带前缀的变量称为“类变量”,@
带前缀的变量称为“实例变量”。
类变量在定义类的所有子类(在继承树的所有子级别)都是静态的。这里的含义是,如果任何子类更改类变量,它将在所有相关子类中更改,直到定义类。
class A; end
class B < A; @@foo = "foo"; end
B.class_variable_get(:@@foo) # => "foo"
A.class_variable_get(:@@foo)
# => raises NameError "uninitialized class variable @@foo in A"
class C < B; end
C.class_variable_get(:@@foo) # => "foo"
class D < C
def self.change_foo(); @@foo = "bar"; end
def change_foo(); @@foo = "baz"; end
end
D.class_variable_get(:@@foo) # => "foo"
class E < D; end
E.class_variable_get(:@@foo) # => "foo"
D.change_foo # => "bar"
D.class_variable_get(:@@foo) # => "bar"
E.class_variable_get(:@@foo) # => "bar"
C.class_variable_get(:@@foo) # => "bar"
B.class_variable_get(:@@foo) # => "bar"
D.new.change_foo # => "baz"
D.class_variable_get(:@@foo) # => "baz"
E.class_variable_get(:@@foo) # => "baz"
C.class_variable_get(:@@foo) # => "baz"
B.class_variable_get(:@@foo) # => "baz"
A.class_variable_get(:@@foo)
# => raises NameError "uninitialized class variable @@foo in A"
至于访问类和实例变量,不使用访问器#instance_variable_get
或::class_variable_get
在定义访问器之前都无法访问。目前,ruby 只有在实例变量上定义访问器的方法,但是为类变量定义适当的方法很简单:
class A
@@foo = "foo"
# the second argument `true` adds the writer method `#bar=`
attr :bar, true
def self.foo(); @@foo; end
def self.foo=(v); @@foo = v; end
def initialize()
@bar = "bar"
end
end
class B < A; end
A.foo # => "foo"
B.foo = "foobar"
A.foo # => "foobar"
B.foo # => "foobar"
a = A.new
a.bar # => "bar"
a.bar = "baz"
a.bar # => "baz"
a.foo
# => raises NoMethodError: undefined method `foo' for #<A:0x ...
您可以在 ruby 核心文档中查看属性访问器方法:http ://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr 。此外,ActiveSupport ( http://rubygems.org/gems/activesupport ) 有 " cattr
" 定义类变量访问器的方法http://api.rubyonrails.org/v3.2.5/classes/Class.html#method-i-cattr_accessor .
这就是简单的东西。下一步是了解“单例类”,也称为“特征类”或“元类”(维基百科:元类)(请记住,ruby 中的一切都是对象,包括类和模块构造)。在这里,我将向您指出 Yehuda Katz 的一篇出色的文章:Ruby 中的元编程:一切都是关于自我的,以及另一个 Stack Overflow 问题:Ruby 中的 class << self idiom。
作为预览:单例类(不要与单例设计模式混淆)允许您访问特定类或模块的方法和实例数据。有关一些相关文档,请参阅核心文档:http ://www.ruby-doc.org/core-1.9.3/Object.html#method-i-singleton_class
class A; end
class B < A;
class << self
def foo() @foo end
def foo=(v) @foo = v; end
end
end
B.foo = "foo"
class C < B; end
A.foo
# => raises NoMethodError: undefined method `foo' for A:Class
B.foo # => "foo"
C.foo # => nil
B.foo = "baz"
B.foo # => "baz"
C.foo # => nil
C.foo = "foo"
C.foo # => "foo"
B.foo # => "baz"
最后,记得使用Ruby-Core 文档。对理解上述内容最有用的是: