2017-10-18 119 views
1

考虑下面的代码:为什么Ruby不能找到调用者类中定义的常量?

class MyClass 
    def foo_via_method 
    foo_method 
    end 

    def foo_via_constant 
    FOO_CONSTANT 
    end 
end 

class SubClass < MyClass 
    FOO_CONSTANT = "foo" 

    def foo_method 
    FOO_CONSTANT 
    end 
end 

两个实例此方法的行为是不同的:

sub_class_instance = SubClass.new 

### THIS WORKS ### 
sub_class_instance.foo_via_method 
# => "foo" 

### THIS DOESN'T ### 
sub_class_instance.foo_via_constant 
# NameError: uninitialized constant MyClass::FOO_CONSTANT 

引用的方法在子类中的版本返回所需的值,而是指版本子类中的常量会引发错误。所以难题是这样的:为什么使用方法的版本可以工作,但使用常量的版本会失败?

+0

这对我来说是一个难以理解的设计。在OOP中,我永远不会期望能够查找在子类中定义的某些东西,除非(我的理解是对此的价值存在争议)一种行为被覆盖的方法。当然,你永远不应该期望只有在子类中定义的东西才能被父类看到。 –

+0

来自我:使用方法的版本 - 在类SubClass的范围内调用常量,以便在另一个函数中调用'FOO_CONSTANT',它试图在MyClass的范围内找到这个常量,以使其可行:写'SubClass :: FOO_CONSTANT' –

回答

2

这是我在实际生产代码中遇到的一个难题。我写了一篇关于this blog post正在发生什么的详细说明。

以下是TLDR:Ruby使用更复杂的算法来解析常量,而不是使用方法。常量查找例程的一个阶段涉及查看超类链中的定义。这个阶段看起来非常像方法查找例程,加深了为什么方法和常量在问题中说明的方式不同的奥秘。

解释是两个超类链例程在它们的起始开始时不同,即哪个类是链的根。

方法查找以self的类开始,其中self是原始方法调用的接收方。在该示例中,sub_class_instance是接收器,​​是查找开始的位置。​​执行foo_method,所以一切都很好。

对于常量,Ruby不会引用接收者,因为常量调用与接收者不相关。相反,常量超类查找从在常量调用发生的词法作用域打开的类开始。在该示例中,已打开的类是MyClass,因此Ruby开始查找常量并从未找到它。

+1

我认为它可以概括为“词汇向外,然后动态向上”,但是IIRC我找到了实际上并不成立的例子。不过,我不记得他们是什么人。 Yugui曾经写过一系列有关Ruby中各种隐式上下文和查找的文章,但不幸的是,虽然她提到了一篇关于不断查找的文章,但该文章从未出现过。 –

+0

当我开始深入YARV时,我惊讶地发现复杂的不断查找。 Ruby的漂亮界面是一些粗糙实现的结果! “自然,不简单。” – hoffm

相关问题