Ruby 类变量异常行为

Ruby 类变量异常行为,ruby,Ruby,当我使用Class.new时,由于某些原因,结果类的类变量相互干扰: # ruby 2.1.6p336 (2015-04-13 revision 50298) [i686-linux] result = Class.new do p self # #<Class:0xb7cd5624> @@foo = 1 def foo p @@foo end end result2 = Class.new do p self # #<Class:0xb7c

当我使用
Class.new
时,由于某些原因,结果类的类变量相互干扰:

# ruby 2.1.6p336 (2015-04-13 revision 50298) [i686-linux]

result = Class.new do
  p self # #<Class:0xb7cd5624>

  @@foo = 1
  def foo
    p @@foo
  end
end

result2 = Class.new do
  p self  # #<Class:0xb7cd54d0>

  @@foo = 2
  def foo
    p @@foo
  end
end

result.class_variable_set(:@@foo, 3)
result.new.foo   # expected 3, output 3
result2.new.foo  # expected 2, output 3
到目前为止,我发现的最接近的线索是:

对类变量的访问被认为是顶级的,因为class关键字没有定义一个类名,该类名将提供保存类变量的范围

(c)

由于您没有使用
class
关键字创建类,因此类变量设置在
Object
上,而不是
Test

(c)

有人能详细描述一下为什么会发生这种情况,以及为什么它与我使用
class
关键字时有如此大的不同吗?

让我们看一下:

result = Class.new do
  p self # #<Class:0xb7cd5624>

  @@foo = 1
  def foo
    p @@foo
  end
end
此时,
result2
已创建为
Class
的实例,并且
Class
的Class变量
@@foo
设置为2

result.class_variable_set(:@@foo, 3)
这将class
class
中名为
@@foo
的类变量设置为值3

result.new.foo   # expected 3, output 3
输出是3,因为上面的
类变量集
@@foo
设置为3

result2.new.foo  # expected 2, output 3
result2
在该语句出现之前已经创建,因此不会执行
@@foo=2
。上面的
result2=Class.new…
构造已经创建了实例
result2.new
创建一个
Class
的新实例,它为
Class
执行构造函数,而不是上面为
result2=Class.new…
(在构建
result2
时已执行的代码)。基
构造函数不知道
@@foo
并且不使用或设置它。所以
@@foo
的值仍然是3


关于警告信息:

警告:从顶级访问类变量

你可能想用谷歌搜索警告信息,因为这里有几个很好的链接可以阅读。特别是,您可能需要查看。这篇简短的文章也解释了我上面描述的内容


如果要为新的动态创建的类使用单独的类变量,可以在创建类后创建/设置类变量。可以通过将类指定给常量来命名这些类:

1.9.2-p330 :017 > result.class_variable_set(:@@bar, 3)
 => 3
1.9.2-p330 :018 > result2.class_variable_set(:@@bar, 4)
 => 4
1.9.2-p330 :019 > R1 = result
 => R1
1.9.2-p330 :020 > R2 = result2
 => R2
1.9.2-p330 :021 > class R1
1.9.2-p330 :022?>   def bar
1.9.2-p330 :023?>     p @@bar
1.9.2-p330 :024?>     end
1.9.2-p330 :025?>   end
 => nil
1.9.2-p330 :026 > class R2
1.9.2-p330 :027?>   def bar
1.9.2-p330 :028?>     p @@bar
1.9.2-p330 :029?>     end
1.9.2-p330 :030?>   end
 => nil
1.9.2-p330 :031 > R1.new.bar
3
 => 3
1.9.2-p330 :032 > R2.new.bar
4
 => 4
1.9.2-p330 :033 >

类变量在整个类层次结构中共享。您可能希望研究如何为您的用例使用

[42] pry(main)> class Parent
[42] pry(main)*   @@foo = "Parent"
[42] pry(main)*  end

[43] pry(main)>  class Thing1 < Parent
[43] pry(main)*    @@foo = "Thing1"
[43] pry(main)*  end

[44] pry(main)>  class Thing2 < Parent
[44] pry(main)*    @@foo = "Thing2"
[44] pry(main)*  end

[45] pry(main)> Thing1.class_eval("@@foo")
=> "Thing2"
[46] pry(main)> Parent.class_eval("@@foo")
=> "Thing2"
[47] pry(main)> Class.class_eval("@@foo")
=> "Thing2"
[42]撬(主)>类父类
[42]撬(主)*@@foo=“父”
[42]撬(主)*端
[43]撬(主)>类件1<母件
[43]撬(主)*@@foo=“Thing1”
[43]撬(主)*端
[44]撬(主)>等级2<母材
[44]撬(主)*@@foo=“Thing2”
[44]撬(主)*端
[45]撬(主)>物1.等级评估(“foo”)
=>“东西2”
[46]撬(主)>父类评估(“foo”)
=>“东西2”
[47]撬(主)>类。类评估(“foo”)
=>“东西2”

块创建一个新的嵌套词法范围,并关闭其周围的词法范围。但是,它们不会改变动态范围

动态范围在块内与块外保持相同,即
self
的值、默认定义对象的值、当前类

顶级的类变量成为
对象的类变量。此外,这两个块共享相同的外部范围。因此,它们共享相同的类变量


请注意,有些方法实际上会更改块的动态范围,例如,
instance\u exec
instance\u eval
更改
self

的值不确定为什么会被否决?此时,结果已创建为类的实例,类的类变量。@@foo设置为1。我想这就是我被绊倒的地方。在文档中写着:创建一个新的匿名(未命名)类。。。()因此,它的类变量应该属于这个新的匿名(未命名)类,不是吗?@stillwaiting这是一个很好的观点。虽然匿名类仍然是未命名和匿名的,但变量是公共的(基本
class
对象的一部分)。如果您命名了新的匿名类,并按名称在类中创建了类变量,那么它们将是分开的。我会更新我的答案以显示这一点。谢谢,但我不确定我是否遵循。你写“如果你想为你的新的,动态创建的类有单独的类变量,你可以在类被创建后创建/设置类变量”,但这就是我要做的:首先我创建一个类的实例,然后我尝试分配一个类变量。如果我从块中删除“@@foo=1”,实际上没有任何变化,输出仍然是一样的。如果我将结果或结果2分配给R1或R2,则两者都不会。我知道使用类块时一切都会顺利进行,但我认为情况并非如此。“我错过了什么吗?”我还在等待,我在区分分配和创建。创建类后,您分配了类变量
@@foo
。在创建新类之后,我创建了
@@bar
类变量,而不是
类的一部分。new
。谢谢。想知道什么是“动态范围”吗?如果我打印“self”,它们都是不同的。外部打印“main”,第一个块内部打印#,第二个块为#。。。。
1.9.2-p330 :017 > result.class_variable_set(:@@bar, 3)
 => 3
1.9.2-p330 :018 > result2.class_variable_set(:@@bar, 4)
 => 4
1.9.2-p330 :019 > R1 = result
 => R1
1.9.2-p330 :020 > R2 = result2
 => R2
1.9.2-p330 :021 > class R1
1.9.2-p330 :022?>   def bar
1.9.2-p330 :023?>     p @@bar
1.9.2-p330 :024?>     end
1.9.2-p330 :025?>   end
 => nil
1.9.2-p330 :026 > class R2
1.9.2-p330 :027?>   def bar
1.9.2-p330 :028?>     p @@bar
1.9.2-p330 :029?>     end
1.9.2-p330 :030?>   end
 => nil
1.9.2-p330 :031 > R1.new.bar
3
 => 3
1.9.2-p330 :032 > R2.new.bar
4
 => 4
1.9.2-p330 :033 >
[42] pry(main)> class Parent
[42] pry(main)*   @@foo = "Parent"
[42] pry(main)*  end

[43] pry(main)>  class Thing1 < Parent
[43] pry(main)*    @@foo = "Thing1"
[43] pry(main)*  end

[44] pry(main)>  class Thing2 < Parent
[44] pry(main)*    @@foo = "Thing2"
[44] pry(main)*  end

[45] pry(main)> Thing1.class_eval("@@foo")
=> "Thing2"
[46] pry(main)> Parent.class_eval("@@foo")
=> "Thing2"
[47] pry(main)> Class.class_eval("@@foo")
=> "Thing2"