本文介绍了类变量的范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在两个类BC中设置一个类变量@@foo,其中两个类都不是另一个的子类,但它们都包含一个公共模块A,似乎分别为BC创建了@@foo,其中A 无法访问:

Setting a class variable @@foo in two classes B and C, where neither is a subclass of the other but they both include a common module A, seems to create @@foo separately for B and C, which A cannot access:

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 时,它作为 BC 的祖先>,BC访问的@@foo成为A@@foo/代码>.

But when @@foo is assigned in A, which works as an ancestor to both B and C, the @@foo that B and C access becomes the @@foo of A.

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

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

What happened to the @@foo of B and C? Are they deleted when any of its ancestor's @@foo is assigned?

推荐答案

此代码出现在 MRI 的 variable.crb_cvar_get 中的 rb_cvar_setrb_cvar_get>:

This code appears in both rb_cvar_set and rb_cvar_get in MRI's 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 是变量名 (@@foo) 的 C 内部表示.

id is the C-internal representation of the variable name (@@foo).

front当前正在访问变量的类 (B/C).

target最远的祖先,其中变量也被定义过 (A).

target is the most distant ancestor in which the variable has also ever been defined (A).

如果 fronttarget 不相同,Ruby 会警告 class 变量 #{id} of #{front}被#{target} 超越.

If front and target are not the same, Ruby warns that class variable #{id} of #{front} is overtaken by #{target}.

然后从front 的RCLASS_IV_TBL 中逐字删除变量名称,以便在后续查找中,对该变量名称的搜索失败"冒泡" 到定义变量的最远祖先.

The variable name is then literally deleted from front's RCLASS_IV_TBL, so that on subsequent lookups, the search for that variable name "falls through" or "bubbles up" to the most distant ancestor in which the variable is defined.

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

Note that this check and deletion happen not just on cvar gets, but on sets as well:

$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's 类变量被 A 取代!

In this example, even though it's A's value of 3 being overwritten by the value 1 being set in B, we still receive the same warning that it's B's class variable being overtaken by A!

虽然对于普通的 Ruby 程序员来说通常更令人惊讶的是发现他们的变量的值在各种可能出乎意料的地方(即在父母"/祖父母"/叔叔"/堂兄"/姐妹"模块和类),触发器和措辞都表明警告实际上是为了通知编码人员变量的真实来源"已更改.

While it is usually more surprising to the average Ruby coder to find that the value of their variable is changing in various, perhaps unexpected, places (i.e. in "parent"/"grandparent"/"uncle"/"cousin"/"sister" modules and classes), the trigger and the wording both indicate that the warning is actually intended to inform the coder that the variable's "source of truth" has changed.

这篇关于类变量的范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-22 14:30