2009-05-05 41 views
61
class Hello 
@hello = "hello" 
    def display 
     puts @hello 
    end 
end 

h = Hello.new 
h.display 

我創建了上面的類。它不打印任何東西。我認爲實例變量@hello是在類聲明期間設置的。但是當我調用顯示方法時,輸出是'零'。什麼是正確的方法來做到這一點?什麼時候Ruby實例變量被設置?

回答

88

實例變量可能是一個有點混亂第一學習Ruby的時候,特別是如果你習慣於像Java另一種面向對象的語言。

你不能簡單地聲明一個實例變量。

除了帶有@符號前綴的符號之外,關於ruby中實例變量最重要的知識之一是,它們在第一次被分配到時會跳入生命中。

class Hello 
    def create_some_state 
    @hello = "hello" 
    end 
end 

h = Hello.new 
p h.instance_variables 

h.create_some_state 
p h.instance_variables 

# Output 
[] 
["@hello"] 

您可以使用方法Object#instance_variables列出對象的所有實例變量。

您通常會在initialize方法中「聲明」並初始化所有實例變量。清楚記錄哪些實例變量應公開可用的另一種方法是使用模塊方法attr_accessor(讀取/寫入),attr_writer(寫入)和attr_reader(讀取)。這些方法將爲列出的實例變量合成不同的訪問器方法。

class Hello 
    attr_accessor :hello 
end 

h = Hello.new 
p h.instance_variables 

h.hello = "hello" 
p h.instance_variables 

# Output 
[] 
["@hello"] 

仍然不創建,直到它的分配給使用合成的Hello#hello=方法的實例變量。

另一個重要問題,如kch所述,您需要知道聲明類時激活的不同上下文。當聲明一個類時,最外面的範圍中的默認接收器(self)將是表示類本身的對象。因此,當您的代碼在類級別上分配給@hello時,您的代碼將首先創建類實例變量。

裏面方法將在其上調用方法的對象,所以你想在對象,這不存在名爲@hello打印實例變量的值(注意,這是完全合法讀取一個不存在的實例變量)。

+2

你說「他們在第一次被分配時就會回到生活中」,然而OP顯示了一個比你的例子中的(明顯)任務更早的例子,並且遇到的問題是所述變量沒有因此而涌入生活。 – kaleidic 2014-07-30 21:21:23

+1

@kaleidic沒錯。對於這個答案中沒有解決OP問題的upvotes數量,我感到有點困惑。實際上,類實例變量`@ hello` *在示例代碼的第2行出現,但問題在於它不是第4行指向的變量。進一步的細節見下面的答案。 – 2014-12-25 18:13:37

42

您需要添加一個initialize方法:在你的代碼

class Hello 
    def initialize 
     @hello = "hello" 
    end 
    def display 
     puts @hello 
    end 
end 

h = Hello.new 
h.display 
21

第一@hello被稱爲類實例變量。

它是常量Hello指向的類對象的實例變量。 (和它是類Class的一個實例。)

從技術上講,當你在class範圍內,您的self設置爲當前的類的對象,並@variables涉及到當前的self。男孩我吮吸解釋這些事情。

您可以通過觀看this collection of $5-each screencasts from The Pragmatic Programmers獲得所有這些和更多的澄清。

(或者你可以要求澄清在這裏,我會嘗試更新。)在紅寶石

+0

一個很好的[文章](http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/)闡述了類級別的實例變量。 – 2014-02-14 12:10:20

9

在「The ruby​​ programming language」一書中有一個明確的描述,閱讀它會很有幫助。我在這裏(由第7.1.16)粘貼:

類定義內,但一個 實例方法定義之外使用的實例變量是一個類實例變量

class Point 
    # Initialize our class instance variables in the class definition itself 
    @n = 0    # How many points have been created 
    @totalX = 0   # The sum of all X coordinates 
    @totalY = 0   # The sum of all Y coordinates 

    def initialize(x,y) # Initialize method 
     @x,@y = x, y  # Sets initial values for instance variables 
    end 

    def self.new(x,y) # Class method to create new Point objects 
     # Use the class instance variables in this class method to collect data 
     @n += 1   # Keep track of how many Points have been created 
     @totalX += x  # Add these coordinates to the totals 
     @totalY += y 

     super    # Invoke the real definition of new to create a Point 
        # More about super later in the chapter 
    end 

    # A class method to report the data we collected 
    def self.report 
     # Here we use the class instance variables in a class method 
     puts "Number of points created: #@n" 
     puts "Average X coordinate: #{@totalX.to_f/@n}" 
     puts "Average Y coordinate: #{@totalY.to_f/@n}" 
    end 
end 

......

因爲類的實例變量是類 對象只是實例變量,我們可以使用ATTR,attr_reader,和attr_accessor爲他們創造 存取方法。

class << self 
    attr_accessor :n, :totalX, :totalY 
end 

有了定義,我們可以參考我們的原始數據Point.n,Point.totalX和Point.totalY這些訪問。

1

我也建議你在看這些前綴類變量「@@」 - 這裏的一些示例代碼向您展示類和實例增值經銷商是不同的:

class Vars 
    @@classvar="foo" 
    def test 
    @instancevar="bar" 
    end 
    def Vars.show 
    puts "classvar: #{@@classvar}" 
    puts "instancevar: #{@instancevar}" 
    end 
    def instance_show 
    puts "classvar: #{@@classvar}" 
    puts "instancevar: #{@instancevar}" 

    end 
end 

# only shows classvar since we don't have an instance created 
Vars::show 
# create a class instance 
vars = Vars.new 
# instancevar still doesn't show b/c it hasn't been initialized 
vars.instance_show 
# initialize instancevar 
vars.test 
# now instancevar shows up as we expect 
vars.instance_show 
4

,我忘了有在Ruby中是一個「類實例變量」的概念。無論如何,OP的問題似乎令人費解,除了對kch答案的暗示之外,迄今爲止的任何答案都沒有真正解決:這是一個範圍問題。 (添加編輯:其實,sris的回答確實是在最後解決了這一點,但我會讓這個答案站在任何地方,因爲我認爲示例代碼可能對理解問題有幫助。)

In a Ruby類,從@變量名可以指變量之一:或者一個實例變量或一個類實例變量,取決於其中在類它被稱爲。這是一個相當微妙的問題。

一個例子將闡明這一點。這裏有一個小的Ruby測試類(在IRB測試的所有代碼):

class T 

    @@class_variable = "BBQ" 
    @class_instance_variable_1 = "WTF" 
    @class_instance_variable_2 = "LOL" 

    def self.class_method 
    puts "@@class_variable   == #{@@class_variable   || 'nil'}" 
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}" 
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}" 
    puts "@instance_variable   == #{@instance_variable   || 'nil'}" 
    end 

    def initialize 
    @instance_variable = "omg" 
    # The following line does not assign a value to the class instance variable, 
    # but actually declares an instance variable withthe same name! 
    @class_instance_variable_1 = "wtf" 
    puts "@@class_variable   == #{@@class_variable   || 'nil'}" 
    # The following two lines do not refer to the class instance variables, 
    # but to the instance variables with the same names. 
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}" 
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}" 
    puts "@instance_variable   == #{@instance_variable   || 'nil'}" 
    end 

    def instance_method 
    puts "@@class_variable   == #{@@class_variable   || 'nil'}" 
    # The following two lines do not refer to the class instance variables, 
    # but to the instance variables with the same names. 
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}" 
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}" 
    puts "@instance_variable   == #{@instance_variable   || 'nil'}" 
    end 

end 

我按照我以爲他們是命名變量,雖然這原來不是總是這樣的情況:

irb> T.class_method 
@@class_variable   == BBQ 
@class_instance_variable_1 == WTF # the value of the class instance variable 
@class_instance_variable_2 == LOL # the value of the class instance variable 
@instance_variable   == nil # does not exist in the class scope 
=> nil 

irb> t = T.new 
@@class_variable   == BBQ 
@class_instance_variable_1 == wtf # the value of the instance variable 
@class_instance_variable_2 == nil # the value of the instance variable 
@instance_variable   == omg 
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf"> 

irb> t.instance_method 
@@class_variable   == BBQ 
@class_instance_variable_1 == wtf # the value of the instance variable 
@class_instance_variable_2 == nil # the value of the instance variable 
@instance_variable   == omg 
=> nil 

irb> T.class_method 
@@class_variable   == BBQ 
@class_instance_variable_1 == WTF # the value of the class instance variable 
@class_instance_variable_2 == LOL # the value of the class instance variable 
@instance_variable   == nil # does not exist in the class scope 
=> nil 

@@class_variable@instance_variable總是按照您的預期行事:前者在類級別上定義,無論是在類方法中引用還是在實例方法中引用,都會在頂部保留指定給它的值。後者僅在類T的對象中獲得值,因此在類方法中,它指的是值爲nil的未知變量。

想象中名爲class_method的類方法根據預期輸出@@class_variable和兩個@class_instance_variable的值,即在類的頂部初始化。但是,在實例方法initializeinstance_method,不同變量的同名被訪問,即實例變量,而不是類實例變量

你可以看到,在initialize方法分配並沒有影響到類實例變量@class_instance_variable_1,因爲class_method以後調用輸出其舊值,"WTF"。相反,方法initialize宣佈一個新的實例變量,一個是命名(誤導性)@class_instance_variable_1。分配給它的值"wtf"由方法initializeinstance_method輸出。

的示例代碼變量@class_instance_variable_2等同於原問題變量@hello:它的聲明和初始化一個類的實例變量,但是當一個實例方法是指名稱的變量,它實際上看到實例變量名稱相同 - 從未聲明的一個,因此其值爲零。