实际上,class_eval 可以计算任意复杂度的表达式。例如,你可以通过计算字符串将其用于向类中添加新方法…

    1. ob = X.new
    2. X.class_eval( 'def hi;puts("hello");end' )
    3. ob.hi #=> "hello"

    回到前面从类外部添加和获取类变量的示例(使用 class_eval);事实证明,还有一些方法可以从类中实现。这些方法称为 class_variable_get(这需要一个表示变量名的符号参数,它返回变量的值)和 class_variable_set(这需要一个表示变量名的符号参数和一个要赋给变量的值作为第二个参数)。这是这些方法的一个示例:

    classvar_getset.rb
    1. class X
    2. def self.addvar( aSymbol, aValue )
    3. class_variable_set( aSymbol, aValue )
    4. end
    5. def self.getvar( aSymbol )
    6. return class_variable_get( aSymbol )
    7. end
    8. X.addvar( :@@newvar, 2000 )
    9. puts( X.getvar( :@@newvar ) ) #=> 2000

    你还可以使用 instance_variable_set 为类和对象在它们被创建后添加实例变量:

    1. ob = X.new
    2. ob.instance_variable_set("@aname", "Bert")

    将此与添加方法的能力相结合,大胆的(或者可能是鲁莽的?)程序员可以完全改变“来自外部”类的内部结构。这里我以类 X 中名为 addMethod 的方法的形式实现了这个方法,它使用 send 方法创建一个新方法 m,该方法使用 define_method 和由 &block 定义的方法体:

    dynamic.rb
    1. def addMethod( m, &block )
    2. self.class.send( :define_method, m , &block )
    3. end
    send 方法调用第一个参数(符号)标识的方法,并将指定的其它参数传递给它。

    虽然从类的特定实例(此处为 ob)调用此方法,但它会影响类本身,因此新定义的方法也可用于后续从 X 类创建的任何实例(此处为 ):

    1. ob2 = X.new
    2. ob2.instance_variable_set("@aname", "Mary")
    3. ob2.xyz

    如果你不关心对象中数据的封装性,你还可以使用 instance_variable_get 方法获取实例变量的值:

      const_get 可以返回常量的值,所以你可以使用此方法获取类名的值,然后附加新方法以从该类创建新对象。这甚至可以通过提示用户输入类名和方法名来为你提供在运行时(runtime)创建对象的方法。通过运行此程序试试这个:

      dynamic2.rb
      1. class X
      2. def y
      3. puts( "ymethod" )
      4. end
      5. end
      6. print( "Enter a class name: ") #<= Enter: X
      7. cname = gets().chomp
      8. ob = Object.const_get(cname).new
      9. p( ob )
      10. print( "Enter a method to be called: " ) #<= Enter: y
      11. ob.method(mname).call