动态处理各种类型变量

之前的章节中我们学习了 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

Tipsbinding是获取所处在的作用域的方法。

解释:因为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_setinstance_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_1name_2name_3,分别返回实例变量@name_1@name_2@name_3。在类被实例化的时候,动态增加了三个实例变量@name_1@name_2@name_3,并赋予1、2、3的初值。

3. 类变量

动态设置类变量所使用到的方法为:class_variable_setclass_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_getconst_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_getconst_set这两个方法要放在module里面才可以使用。另外不要对常量进行重复赋值。

6. 小结

本章节我们学习了:

  1. 局部变量使用local_variable_setlocal_variable_get
  2. 实例变量使用instance_variable_setinstance_variable_get
  3. 类变量使用class_variable_setclass_variable_get
  4. 常量使用const_setconst_get
  5. 全局变量不建议动态创建和调用;
  6. 所有动态处理变量都可以使用eval,但是不推荐使用。