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)
这将classclass
中名为@@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"