[英]Accessing Ruby Class Variables with class_eval and instance_eval
我有以下內容:
class Test
@@a = 10
def show_a()
puts "a: #{@@a}"
end
class << self
@@b = '40'
def show_b
puts "b: #{@@b}"
end
end
end
為什么以下工作:
Test.instance_eval{show_b}
b: 40
=> nil
但我不能直接訪問@@b
?
Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object
同樣,以下工作
t = Test.new
t.instance_eval{show_a}
a: 10
=> nil
但是以下失敗了
t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object
我不明白為什么我不能直接從instance_eval
塊訪問類變量。
我在RubyKaigi派對期間向Matz提出了同樣的問題。 我喝醉了一半,但他非常清醒,所以你可以把它作為明確的答案。
Anton是對的 - 你不能通過instance_eval()訪問類變量的原因是“只是因為”。 即使class_eval()也有同樣的問題(Matz本人對class_eval()並不完全確定,直到我告訴他我已經嘗試過了)。 更具體地說:范圍方面,類變量更像是常量而不是實例變量,因此切換self(如instance_eval()和class_eval()do)在訪問它們時不會有任何區別。
通常,完全避免類變量可能是個好主意。
編輯:下面的代碼用1.8.7和1.9.1測試......似乎情況與1.9.2再次不同:/
實際情況並非如此直截了當。 根據您使用的是1.8還是1.9以及是否使用class_eval
或instance_eval
,行為存在差異。
以下示例詳細說明了大多數情況下的行為。
我還包括了常量的行為,這是好的衡量標准,因為它們的行為類似於類變量,但不完全相同。
Ruby 1.8中的class_eval
:
class Hello
@@foo = :foo
end
Hello.class_eval { @@foo } #=> uninitialized class variable
Ruby 1.9中的class_eval
:
Hello.class_eval { @@foo } #=> :foo
因此,當使用class_eval
時,在1.9(但不是1.8) 中查找類變量
Ruby 1.8 和 1.9中的instance_eval
Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable
當使用instance_eval
時,似乎沒有在1.8 或 1.9中查找類變量
有趣的是常量的情況:
Ruby 1.8中的class_eval
class Hello
Foo = :foo
end
Hello.class_eval { Foo } #=> uninitialized constant
Ruby 1.9中的class_eval
Hello.class_eval { Foo } #=> :foo
因此,與類變量一樣,常量在1.9中查找,但在class_eval
不在 1.8中class_eval
Ruby 1.8中的instance_eval
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant
Ruby 1.9中的instance_eval
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo
似乎常量查找與Ruby 1.9的類變量查找不太相似。 Hello
實例確實可以訪問常量,而Hello
類則不能。
好吧,可能最好的答案是“只是因為”:簡而言之,instance_eval創建了一種使用給定對象綁定調用的單例proc。 我同意這聽起來有點奇怪,但事實就是如此。
如果使用字符串執行instance_eval,您甚至會收到警告,告知您的方法嘗試訪問類變量:
irb(main):038:0> Test.new.instance_eval "@@a"
(eval):1: warning: class variable access from toplevel singleton method
NameError: (eval):1:in `irb_binding': uninitialized class variable ...
Ruby 2.1
這是我發現訪問類變量時最簡潔和語義正確的方法:
class Hello
@@foo = :foo_value
end
Hello.class_variable_get :@@foo #=> :foo_value
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.