动态处理各种类型变量
之前的章节中我们学习了 Ruby 中一共有局部、实例、类、全局 4 种变量,以及一种常量,今天让我们来学习一下在元编程中,如何动态获取这些变量。
1. 局部变量
局部变量以往我们使用eval
来获取。
实例:
b = binding
(1..3).each do |num|
b.eval("a_#{num} = #{num}")
end
(1..3).each do |num|
puts b.eval("a_#{num}")
end
# ---- 输出结果 ----
1
2
3
Tips:
binding
是获取所处在的作用域的方法。
解释:因为each
后面有一个 block,两个 block 里面的作用域不共享(详细在作用域章节中会学习到),这里我们用了binding
,让eval
可以共享顶级作用域。分别动态定义了 a_1~a_3 的方法,并动态输出出来。
在 Ruby 2.1.0 之后,增加了local_variable_set
、和local_variable_get
方法。
实例:
b = binding
(1..3).each do |num|
b.local_variable_set("a_#{num}".to_sym, num)
end
(1..3).each do |num|
puts b.local_variable_get("a_#{num}".to_sym)
end
# ---- 输出结果 ----
1
2
3
它们本质上是相同的。
2. 实例变量
同样我们可以使用eval
来获取,只需要在局部变量前加一个@
,本着少使用eval
的原则,这里我只给大家讲解我们更常用的instance_variable_set
和instance_variable_get
。
实例:
class Person
def initialize
(1..3).each do |num|
instance_variable_set("@name_#{num}".to_sym, num)
end
end
(1..3).each do |num|
define_method "name_#{num}".to_sym do
instance_variable_get("@name_#{num}".to_sym)
end
end
end
person = Person.new
p person.name_1
p person.name_2
p person.name_3
# ---- 输出结果 ----
1
2
3
解释:在类被定义的时候,动态创建3个方法:name_1
、name_2
、name_3
,分别返回实例变量@name_1
、@name_2
、@name_3
。在类被实例化的时候,动态增加了三个实例变量@name_1
,@name_2
,@name_3
,并赋予1、2、3的初值。
3. 类变量
动态设置类变量所使用到的方法为:class_variable_set
和class_variable_get
。
实例:
class Person
(1..3).each do |num|
class_variable_set("@@name_#{num}".to_sym, num * 3)
end
def initialize
(1..3).each do |num|
instance_variable_set("@name_#{num}".to_sym, self.class.class_variable_get("@@name_#{num}".to_sym))
end
end
(1..3).each do |num|
define_method "name_#{num}".to_sym do
instance_variable_get("@name_#{num}".to_sym)
end
end
end
person = Person.new
p person.name_1
p person.name_2
p person.name_3
# ---- 输出结果 ----
3
6
9
解释:这次我们在类初始化的时候创建了@@name_1
、@@name_2
、@@name_3
三个变量赋予3、6、9三个初值。在类实例化的时候使用类变量分别对@name_1
、@name_2
、@name_3
进行初始化。注意这里class_variable_get
是类方法,所以要使用self.class
来获取。
4. 全局变量
可以使用eval
,但是不建议这样做,因为非常难以调试和维护这些全局变量。
5. 常量
动态操作常量我们使用const_get
和const_set
两个方法。
module Test
(1..3).each do |num|
const_set "CONSTANT_#{num}", num
end
(1..3).each do |num|
const_set "CONSTANT_#{num}"
end
end
# ---- 输出结果 ----
1
2
3
注意事项:const_get
和const_set
这两个方法要放在module
里面才可以使用。另外不要对常量进行重复赋值。
6. 小结
本章节我们学习了:
- 局部变量使用
local_variable_set
和local_variable_get
; - 实例变量使用
instance_variable_set
和instance_variable_get
; - 类变量使用
class_variable_set
和class_variable_get
; - 常量使用
const_set
和const_get
; - 全局变量不建议动态创建和调用;
- 所有动态处理变量都可以使用
eval
,但是不推荐使用。