加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby – 类变量的范围

发布时间:2020-12-16 21:02:21 所属栏目:百科 来源:网络整理
导读:在两个B和C类中设置一个类变量@@ foo,其中两个类都不是另一个的子类,但它们都包含一个公共模块A,似乎分别为B和C创建@@ foo,A无法访问: module A; endclass B; include A; @@foo = 1 endclass C; include A; @@foo = 2 endmodule A; p @@foo end # = NameErr
在两个B和C类中设置一个类变量@@ foo,其中两个类都不是另一个的子类,但它们都包含一个公共模块A,似乎分别为B和C创建@@ foo,A无法访问:
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

但是,如果在A中分配@@ foo,它作为B和C的祖先,那么B和C访问的@@ foo成为A的@@ foo.

module A; @@foo = 3 end
class B; p @@foo end  # => 3
class C; p @@foo end  # => 3

B和C的@@ foo怎么了?当它的任何祖先的@@ foo被分配时它们是否被删除?

解决方法

此代码出现在MRI的variable.c中的rb_cvar_set和rb_cvar_get中:
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是最远的祖先,变量也曾被定义过(A).

如果front和target不相同,Ruby会警告#{front}的类变量#{id}被#{target}超越.

然后从前面的RCLASS_IV_TBL中逐字删除变量名,以便在后续查找中,搜索该变量名称“通过”或“冒泡”到定义变量的最远祖先.

请注意,此检查和删除不仅发生在cvar gets上,还发生在集合上:

$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被B中设置的值1覆盖,我们仍然会收到相同的警告:B的类变量被A超越!

虽然普通的Ruby编码器通常会发现他们的变量值在各种各样的,也许是意想不到的地方(即“父母”/“祖父母”/“叔叔”/“堂兄”/“姐妹”)发生变化,这一点更令人惊讶模块和类),触发器和措辞都表明警告实际上是为了通知编码人变量的“真实来源”已经改变.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读