2014-02-20 65 views
6

假設使用類變量的簡單Ruby程序,混亂關於Ruby類變量

class Holder 
     @@var = 99 
     def Holder.var=(val) 
     @@var = val 
     end 
     def var 
     @@var 
     end 
    end 

    @@var = "top level variable" 

    a = Holder.new 
    puts a.var 

我想結果應該是99,但產量不99。我想知道爲什麼。由於類變量的範圍是類,因此我假定行@@var = "top level variable"不會影響類中的變量。

回答

6

@@var是一個類變量Holder@@var在頂層是不同的同名變量@@varHolder,它是你正在創建一個全新的類變量Object類。現在@@var與父類的子類共享。 ObjectHolder類的父類。在Ruby中,如果您沒有明確定義任何自定義類的超類,則使用class關鍵字定義,然後Object類將成爲您剛創建的類的默認超類。

在您的發佈示例中,您正在頂層定義類Object中的新類變量,就像您一樣。正如通過繼承,它現在可用於類Holder

我的意思是,當你寫的下面:

class Holder 
    @@var = 99 
    def Holder.var=(val) 
    @@var = val 
    end 
    def var 
    @@var 
    end 
end 

@@var尚未添加到Object類。現在在頂層,當你寫的下面一行:

@@var = "top level variable" 

這意味着,你將它添加到Object類,並同時更新舊變量(@@var)相同的名稱作爲一個Holder類,這個新一,你剛纔定義在Object班的範圍內。共享變量,僅可見類(B),在那裏你將它定義和派生類(C)它的子類(C),後代(D),等等

記住,類變量。但在類(B,如下所示)的超類A以下)中不可見,直到您在子類(B)中定義了同名變量(A) 。

class A 
end 

class B < A 
    @@var = 10 
end 

class C < B 
end 

class D < C 
end 

A.class_variable_get(:@@var) # uninitialized class variable @@var in A (NameError) 
B.class_variable_get(:@@var) # 10 
C.class_variable_get(:@@var) # 10 
D.class_variable_get(:@@var) # 10 
1

我想擴大在其他人所說,特別是,與類的實例變量比較使用類變量。讓我們先從這一點:

class Holder1 
end 

class Holder2 < Holder1 
    @@var = 99 

    # Create a class setter and a getter for the class variable 
    def Holder2.var=(val) 
    @@var = val 
    end 

    def Holder2.var 
    @@var 
    end 

    # Create a instance getter for the class variable 
    def var 
    @@var 
    end 
end 

class Holder3 < Holder2 
end 

Holder2.var  #=> 99 
Holder2.var = 1 
Holder3.var  #=> 1 
Holder1.var  #=> NoMethodError: undefined method `var' for Holder1:Class 
Holder2.new.var #=> 1 
Holder3.new.var #=> 1 

Holder3.var = 2 
Holder3.var  #=> 2 
Holder2.var  #=> 2 
Holder3.new.var #=> 2 
Holder2.new.var #=> 2 

旁白:前兩種方法通常會被寫成這樣:

def self.var=(val) 
    @@var = val 
    end 

    def self.var 
    @@var 
    end 

這工作,因爲self =>Holder2在創建方法時。如果您決定重命名班級,則不需要修改班級名稱,也不需要進行更改。

現在這個(使用類變量)可能正是你想要的行爲。也就是說,如果您希望子類查看變量並能夠更改它,則需要一個類變量。

但是,如果你想每個子類有自己的變量,它可以既看不見也由子類改變,你需要使用一個類實例變量@var,而不是一類變量,@@var。 (從技術上講,這是不正確的,因爲你可以在你的程序的任何地方使用Holder2.instance_variable_get(:@var)Holder2.instance_variable_set(:@var))。

比較下面的代碼和上面的代碼的結果。我已經包含一個實例變量,其名稱與類實例變量@var相同,以說明它們與@night@day不同。

class Holder1 
end 

class Holder2 < Holder1 
    # Create an accessor for the instance variable 
    attr_accessor :var 

    # Initialize class instance variable 
    @var = 99 

    # Create an accessor for the class instance variable 
    class << self 
    puts "self in 'class << self': #{self}" 
    attr_accessor :var 
    def our_dog 
     "Diva" 
    end 
    end 

    # Create a class method 
    def self.our_cat 
    puts "self in 'self.our_cat())' def: #{self}" 
    "Teagan" 
    end 

    # Create an instance setter and a getter for the class instance variable 
    def c_ivar=(val) 
    self.class.var = val 
    end 

    def c_ivar 
    self.class.var 
    end 
end 

class Holder3 < Holder2 
end 

       #=> self in 'class << self': #<Class:Holder2> 

Holder2.var  #=> 99 
h2 = Holder2.new 
h2.var   #=> nil 

Holder2.var = 1 
Holder2.var  #=> 1 
h2.c_ivar  #=> 1 
h2.c_ivar = 2 
Holder2.var  #=> 2 

h2.var   #=> nil 
h2.var = 3 
h2.var   #=> 3 
Holder2.var  #=> 2 

Holder3.var  #=> nil 
Holder1.var  #=> NoMethodError: undefined method `var' for Holder1:Class 

Holder3.var = 4 
Holder3.var  #=> 4 
Holder2.var  #=> 2 
h3 = Holder3.new 
h3.c_ivar  #=> 4 
h2.c_ivar  #=> 2 

Holder3.our_dog #=> "Diva" 
Holder3.our_cat #=> "self in 'self.our_cat())' def: Holder3" 
       #=> "Teagan" 

Holder1.var拋出一個異常,因爲這個類有Holder2的類實例變量[email protected]進不去。相比之下,Holder3.var的第一次使用返回nil,因爲變量存在Holder3,通過繼承,但尚未初始化。

表達class << self改變從Holder2selfHolder2的單例類(又名元類),直到下一個end。請注意,Ruby表示Holder2的單例類爲#<Class:Holder2>。另外,我們不需要在self.前面加上my_dog,因爲在創建方法時,selfHolder2的單例類。

注意:

Holder2.singleton_methods #=> [:var, :var=, :our_dog, :our_cat] 

這表明our_cat()Holder2的方法的單例類,即使selfHolder2(而不是Holder2的單例類',#<Class:Holder2>)構建的方法時。出於這個原因,有人說,在技術上,"there is no such thing as a class method"。這也告訴我們,我們可以將my_cat的定義移到class << self構造中(並丟棄self.)。

另一種常見的方式來實例變量和方法添加到Holder2的單例類是extend M更換class << self ... end並創建一個模塊:

module M 
    attr_accessor :var 
    def our_dog 
    "Diva" 
    end 
end 

Object#extend混合這些實例變量和方法爲Holder2的單例類。