class Hello
@hello = "hello"
def display
puts @hello
end
end
h = Hello.new
h.display
我创建了上面的课程。它不打印任何东西。我认为在类声明期间设置了实例变量@hello。但是当我调用display方法时输出为'nil'。这样做的正确方法是什么?
在第一次学习Ruby时,ruby中的实例变量可能有点令人困惑,特别是如果你习惯于另一种像Java这样的OO语言。
您不能简单地声明实例变量。
除了带有@符号前缀的符号之外,了解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
方法列出对象的所有实例变量。
您通常在initize方法中“声明”并初始化所有实例变量。另一种清楚地记录应该公开可用的实例变量的方法是使用模块方法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
时,您的代码将首先创建一个类实例变量。
内部方法self将是调用该方法的对象,因此您尝试在对象中打印名称为@hello
的实例变量的值,该值不存在(请注意,读取不存在的内容完全合法)实例变量)。
你需要添加一个initialize
方法:
class Hello
def initialize
@hello = "hello"
end
def display
puts @hello
end
end
h = Hello.new
h.display
代码中的第一个@hello
称为类实例变量。
它是类对象的实例变量,常量Hello
指向它。 (这是类Class
的一个实例。)
从技术上讲,当你在class
范围内时,你的self
被设置为你当前类的对象,@variables
属于你当前的self
。男孩我很蠢地解释这些事情。
你可以通过观看this collection of $5-each screencasts from The Pragmatic Programmers获得所有这些并且更加清晰。
(或者你可以在这里要求澄清,我会尝试更新。)
在“红宝石编程语言”一书中有一个清晰的描述,阅读它将非常有帮助。我把它贴在这里(来自第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。
我忘记了Ruby中有一个“类实例变量”概念。在任何情况下,OP的问题似乎都令人费解,并且在迄今为止的任何答案中都没有真正解决,除了kch的答案中的暗示:它是范围问题。 (在编辑中添加:实际上,sris的答案确实在最后解决了这一点,但无论如何我都会让这个答案成立,因为我认为示例代码可能对理解问题很有帮助。)
在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
s的值,也就是说,在类的顶部初始化。但是,在实例方法initialize
和instance_method
中,访问了同名的不同变量,即实例变量,而不是类实例变量。
您可以看到initialize
方法中的赋值不会影响类实例变量@class_instance_variable_1
,因为后来调用class_method
会输出其旧值"WTF"
。相反,方法initialize
声明了一个新的实例变量,一个也被命名为(误导性地)@class_instance_variable_1
。分配给它的值"wtf"
由initialize
和instance_method
方法输出。
示例代码中的变量@class_instance_variable_2
等效于原始问题中的变量@hello
:它被声明并初始化为类实例变量,但是当实例方法引用该名称的变量时,它实际上看到一个具有相同名称的实例变量 - 一个从未申报的,所以它的价值是零。
我还建议查看以“@@”为前缀的类变量 - 这里有一些示例代码,向您展示类和实例变量是如何不同的:
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