类变量奇怪的行为

时间:2015-06-12 09:57:31

标签: 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: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

为什么呢?引擎盖下发生了什么?

此外还有相关的警告信息,但我无法理解它们的含义,也无法找到好的描述。

warning: class variable access from toplevel

到目前为止,我发现的最接近的线索是:

  

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

(c)http://japhr.blogspot.ru/2009/06/more-information-about-class-variables.html

  

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

(c)https://stackoverflow.com/a/10712458/1432640

有人可以详细说明为什么会发生这种情况以及为什么它与我使用class关键字时的情况如此不同?

3 个答案:

答案 0 :(得分:1)

让我们一起来看看:

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

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

此时,result已创建为Class的实例,Class的类变量@@foo设置为1。

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

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

此时,result2已创建为Class的实例,Class的类变量@@foo设置为2.

result.class_variable_set(:@@foo, 3)

这会将名为Class的班级@@foo中的类变量设置为值3。

result.new.foo   # expected 3, output 3

输出为3,因为@@foo从上面的class_variable_set设置为3。

result2.new.foo  # expected 2, output 3
在此语句发生之前已经创建了

result2,因此不会执行@@foo = 2result2 = Class.new ...上面的构造已经创建了实例。 result2.new创建了一个Class的新实例,它会为Class而不是上面为result2 = Class.new ...创建的代码执行构造函数在你构建result2)之后已经执行了。基础Class构造函数不了解@@foo ,也不使用它或设置它。所以@@foo的值仍为3。

关于警告信息:

  

警告:来自toplevel的类变量访问

您可能希望Google搜索警告消息,因为有几个很好的链接可供阅读。特别是,您可能希望查看The many gotchas of Ruby class variables。这篇简短的文章也解释了我上面所描述的内容。

<小时/> 如果要为新的动态创建的类创建单独的类变量,可以在创建类之后创建/设置类变量。您可以通过将类指定为常量来命名类:

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 >

答案 1 :(得分:0)

类变量在整个类层次结构中共享。您可能希望将class instance variables用于您的用例。

$('#delivery_option_22_6').on('change', function() {
    if ($(this).is(':checked')) {
        // Show popup window
        alert("selected");
    }
});

Ruby, class variables and inheritance ? super class @@var gets changed?

答案 2 :(得分:0)

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

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

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

请注意,某些方法实际上执行更改了块的动态范围,例如: instance_execinstance_eval更改self的值。