类变量的范围

时间:2014-02-20 12:33:04

标签: ruby class-variables

在两个类@@fooB中设置一个类变量C,其中两个类都不是另一个的子类,但它们都包含一个公共模块A,似乎单独为@@fooB创建CA无法访问:

module A; end
class B; include A; @@foo = 1 end
class C; include A; @@foo = 2 end

module A; p @@foo end # => NameError: uninitialized class variable @@foo in A
class B; p @@foo end  # => 1
class C; p @@foo end  # => 2

但是,@@foo分配了AB作为C@@foo的祖先,B C @@foo并且A访问权限成为module A; @@foo = 3 end class B; p @@foo end # => 3 class C; p @@foo end # => 3 的{​​{1}}。

@@foo

BC的{​​{1}}发生了什么?它们的任何祖先@@foo被分配时是否被删除?

2 个答案:

答案 0 :(得分:2)

这段代码同时出现在MRI rb_cvar_set的{​​{1}}和rb_cvar_get中:

variable.c

if (front && target != front) { st_data_t did = id; if (RTEST(ruby_verbose)) { rb_warning("class variable %"PRIsVALUE" of %"PRIsVALUE" is overtaken by %"PRIsVALUE"", QUOTE_ID(id), rb_class_name(original_module(front)), rb_class_name(original_module(target))); } if (BUILTIN_TYPE(front) == T_CLASS) { st_delete(RCLASS_IV_TBL(front),&did,0); } } 是变量名称(id)的C内部表示。

@@foo是当前正在访问的变量的类front / B)。

C最遥远的祖先,其中变量也已经定义target)。

如果Afront不一样,Ruby会警告target

变量名称从class variable #{id} of #{front} is overtaken by #{target}的RCLASS_IV_TBL逐字删除,以便在后续查找中,搜索该变量名“通过”“冒泡”到定义变量的最远祖先。


请注意,此检查和删除不仅发生在cvar上,而且发生在

front

在此示例中,即使它的 $VERBOSE = true module A; end class B; include A; @@foo = 1; end # => 1 module A; @@foo = 3 end # => 3 class B; p @@foo = 1 end # => 1 #=> warning: class variable @@foo of B is overtaken by A module A; p @@foo end # => 1 的值 A 被值覆盖 3设置1,我们仍会收到相同的警告B的{​​{1}}类变量被B超越!

虽然普通的Ruby编码器通常会发现他们的变量值在不同的,可能是意外的地方(即“父母”/“祖父母”/“叔叔”/“堂兄”/ “姐妹”模块和类),触发器和措辞都表明警告实际上是为了通知编码人员变量的“真实来源”已经改变。

答案 1 :(得分:1)

下面我的笔记取自Metaprogramming Ruby (by Paolo Perrotta),正好在我遇到你的问题时,我正好在读书。我希望这些摘录(页码放在括号中)和我的解释对你有帮助。

请注意,类变量类实例变量不同。

  

Class实例变量属于类Class的对象,并且是   只能由类本身访问 - 不是通过实例或通过实例访问   子类。 (106)

另一方面,类变量属于类层次结构。这意味着它属于任何类以及该类的所有后代。

以下是作者的一个例子:

@@v = 1

class MyClass
  @@v = 2
end

@@v    # => 2
  

你得到这个结果,因为类变量并不真正属于   类 - 它们属于类层次结构。由于@@ v在中定义   main的上下文,它属于main'sObject ...以及   所有Object的后代。 MyClass继承自Object,所以   它最终共享相同的类变量。 (107)

但是,由于您的具体问题不仅要与课程有关,还要与模块有关:

  

当你在一个类中包含一个模块时,Ruby会创建一个匿名的   包装模块并在其中插入匿名类的类   链,就在包含类本身之上。 (26)

因此,当您查看B.ancestors时,您会看到:

=> [B, A, Object, Kernel, BasicObject]

同样,对于C.ancestors,您会看到:

=> [C, A, Object, Kernel, BasicObject]

如果我们记住类变量属于类层次结构,那么类变量@@foo,只要它在Module A中定义(等等,匿名类就在{{1}之上B包含B)后立即创建的内容将属于A(以及B,因为它包含C)。< / p>

简单地说:

  1. A仅在@@fooB中定义(但不在C中),A有一个类变量B }与@@foo中的类变量@@foo不同。这是因为类变量只能被该类和所有后代访问。但是CB通过他们的祖先C相关联,而不是通过他们的后代。
  2. 只要A中定义了@@foo,该类变量就会被A的所有后代继承 - 即AB。从现在开始,类C中对@@foo的引用实际上是引用属于B的类变量。 A中定义的原始@@foo已被覆盖替换(由其祖先接管)。 B中的@@foo也发生了同样的情况。 CB可以同时写入和读取同一个类变量C,因为它属于它们的共同祖先@@foo
  3. 此时,AAB中的任何人都可以修改C。例如:

    @@foo