什么时候Ruby实例variables被设置?

class Hello @hello = "hello" def display puts @hello end end h = Hello.new h.display 

我创build了上面的类。 它不打印任何东西。 我认为实例variables@hello是在类声明期间设置的。 但是当我调用显示方法的输出是“零”。 什么是正确的方法来做到这一点?

Ruby中的实例variables在第一次学习Ruby的时候可能会有点混乱,特别是如果你习惯了Java这样的其他OO语言。

你不能简单地声明一个实例variables。

除了带有@符号前缀的符号外,了解ruby中的实例variables最重要的事情之一是, 它们在第一次被分配到时会弹回生命中

 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列出Object#instance_variables所有实例variables。

您通常在initialize方法中“声明”并初始化所有实例variables。 清楚地logging哪些应该公开的实例variables的另一种方法是使用Module方法attr_accessor (读/写), attr_writer (write)和attr_reader (read)。 这些方法将为列出的实例variables合成不同的访问器方法。

 class Hello attr_accessor :hello end h = Hello.new p h.instance_variables h.hello = "hello" p h.instance_variables # Output [] ["@hello"] 

实例variables在被分配到使用合成的Hello#hello=方法之前还没有被创build。

另一个重要的问题,如kch描述的,是你需要知道在声明一个类时活跃的不同的上下文。 当声明一个类时,最外层的默认receiver(self)将是代表这个类本身的对象。 因此,你的代码将在类级别分配给@hello时首先创build一个类实例variables。

内部方法self将是调用该方法的对象,因此您正尝试在对象中打印名称为@hello的实例variables的值,该值不存在(请注意,读取非现有的实例variables)。

你需要添加一个initialize方法:

 class Hello def initialize @hello = "hello" end def display puts @hello end end h = Hello.new h.display 

代码中的第一个@hello被称为类实例variables。

它是常量Hello指向的类对象的实例variables。 (这是Class一个实例。)

从技术上讲,当你@variables class范围内时,你的self被设置为当前类的对象, @variables属于你当前的self 。 男孩我吮吸解释这些事情。

你可以通过观看“实用程序员”每集5美元的屏幕录像来获得所有这些和更多的澄清。

(或者你可以在这里要求澄清,我会尝试更新。)

在“The ruby​​ programming language”一书中有一个明确的描述,读起来会很有帮助。 我把它粘贴在这里(从7.1.16章节):

在类定义中使用但在实例方法定义之外使用的实例variables类实例variables

 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 

……

由于类实例variables只是类对象的实例variables,我们可以使用attr,attr_reader和attr_accessor为它们创build访问器方法。

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

通过定义这些访问器,我们可以将我们的原始数据引用为Point.n,Point.totalX和Point.totalY。

我忘了Ruby中有一个“类实例variables”的概念。 无论如何,OP的问题似乎令人费解,除了对kch答案的暗示之外,还没有真正解决过这个问题:这是一个范围问题。 (在编辑中join:实际上,sris的答案最后提到了这一点,但我会让这个答案站在任何地方,因为我认为示例代码可能对理解问题有用。

在Ruby类中,以@开头的variables名称可以引用两个variables中的一个:或者是一个实例variables,或者是一个类实例variables ,具体取决于它引用的类中的什么地方。 这是一个相当微妙的问题。

一个例子将澄清这一点。 这里有一个小小的Rubytesting类(在irb中testing的所有代码):

 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 

我根据自己的想法给variables命名,尽pipe事实并非总是如此:

 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的对象中获得一个值,所以在一个类方法中,它指的是一个未知的variables,其值nil

想象中命名为class_method的类方法按预期的class_method输出@@class_variable和两个@class_instance_variable的值,即在类的顶部初始化。 但是,在实例方法initializeinstance_method ,访问同名的 不同variables,即实例variables,而不是类实例variables

您可以看到initialize方法中的赋值不会影响类实例variables@class_instance_variable_1 ,因为稍后的class_method调用class_method输出其旧值"WTF" 。 相反,方法initialize 声明了一个新的实例variables, 被命名为(误导性) @class_instance_variable_1 。 分配给它的值"wtf"由方法initializeinstance_method输出。

示例代码中的@class_instance_variable_2variables@hello于原始问题中的variables@hello :它是作为类实例variables声明和初始化的,但是当实例方法引用该名称的variables时,实际上会看到一个实例variables同名 – 从来没有宣布过,所以它的价值是零。

我还build议查看以“@@”为前缀的类variables – 下面是一些示例代码,向您展示类和实例variables是如何不同的:

 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