49

我是编程新手。现在我正在学习Ruby。据我了解,全局变量是在全局命名空间中定义的(因此在任何类或函数之外)。我正在读一些东西,它说全局变量$在它们之前有一个符号。这意味着什么?这是否意味着当我定义一个函数或类并想要引用我的全局变量(假设它是edmund = 123)时,我必须像这样引用它:$edmund

所以:

edmund = 123
def my_function()
  456 + $edmund
end

类变量(以 开头的@@)也像实例变量 ( @),您可以通过调用它们来访问它们Class.classvariable吗?他们的目的是什么?

4

4 回答 4

53

全局范围是覆盖整个程序的范围。全局范围由全局变量享有,它们可以通过它们的初始美元符号 ($) 字符来识别。它们随处可见,创建自己的全局变量可能很诱人,尤其是对于初学者来说。但它们并不总是一个好主意。

$gvar = "I'm a global!"
class C
    def examine_global
        puts $gvar
    end
end

c = C.new
c.examine_global # I'm a global!

类变量以两个 at 符号开头:例如,@@var。尽管它们的名字,类变量不是类范围的。相反,它们是类层次结构范围的。最简单的,类变量背后的想法是它提供了一种在类和该类的实例之间共享的存储机制,并且对任何其他对象都不可见。

class Parent
    @@value = 100
end

class Child < Parent
    @@value = 200
end

class Parent
    puts @@value
end

打印出来的是 200。 Child 类是 Parent 的子类,这意味着 Parent 和 Child 共享相同的类变量——不是同名的不同类变量,而是相同的实际变量。当您在 Child 中分配 @@value 时,您正在设置在整个层次结构中共享的唯一一个 @@value 变量,即由 Parent 和 Child 以及它们中的任何一个的任何其他后代类共享。


并在应得的地方给予赞扬 - 这个解释来自 David A Black 的“The Well Grounded Rubyist”,这是了解 Ruby 的最佳资源之一。

于 2012-08-24T16:56:52.793 回答
10

很好的问题。不幸的是,您刚刚跳入了一个兔子洞,但您最终必须在 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 文档。对理解上述内容最有用的是:

于 2012-08-24T18:51:18.643 回答
4

美元符号是变量名的一部分,因此必须像这样声明:

$edmund = 123

@实例变量和类变量的情况相同:它们的名称以or开头@@

于 2012-08-24T16:17:52.330 回答
0

查看这些文章:

http://www.rubyist.net/~slagell/ruby/globalvars.html

如何在 Ruby 中使用全局变量或常量值?

于 2012-08-24T16:18:54.630 回答