簡體   English   中英

使用class_eval和instance_eval訪問Ruby類變量

[英]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_evalinstance_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM