添加变量和方法
module_eval
和 class_eval
方法也可用于获取类变量的值(但请记住,你越这么做,代码就越依赖于类的实现细节,从而破坏封装性):
Y.class_eval( "@@x" )
实际上,class_eval
可以计算任意复杂度的表达式。例如,你可以通过计算字符串将其用于向类中添加新方法…
ob = X.new
X.class_eval( 'def hi;puts("hello");end' )
ob.hi #=> "hello"
回到前面从类外部添加和获取类变量的示例(使用 class_eval
);事实证明,还有一些方法可以从类中实现。这些方法称为 class_variable_get
(这需要一个表示变量名的符号参数,它返回变量的值)和 class_variable_set
(这需要一个表示变量名的符号参数和一个要赋给变量的值作为第二个参数)。这是这些方法的一个示例:
class X
def self.addvar( aSymbol, aValue )
class_variable_set( aSymbol, aValue )
end
def self.getvar( aSymbol )
return class_variable_get( aSymbol )
end
end
X.addvar( :@@newvar, 2000 )
puts( X.getvar( :@@newvar ) ) #=> 2000
要获取类变量名称列表作为字符串数组,请使用 class_variables
方法:
p( X.class_variables ) #=> ["@@abc", "@@newvar"]
你还可以使用 instance_variable_set
为类和对象在它们被创建后添加实例变量:
ob = X.new
ob.instance_variable_set("@aname", "Bert")
将此与添加方法的能力相结合,大胆的(或者可能是鲁莽的?)程序员可以完全改变“来自外部”类的内部结构。这里我以类 X 中名为 addMethod
的方法的形式实现了这个方法,它使用 send
方法创建一个新方法 m
,该方法使用 define_method
和由 &block
定义的方法体:
def addMethod( m, &block )
self.class.send( :define_method, m , &block )
end
send
方法调用第一个参数(符号)标识的方法,并将指定的其它参数传递给它。现在,X 对象可以调用 addMethod
将新方法插入到 X 类中:
ob.addMethod( :xyz ) { puts("My name is #{@aname}") }
虽然从类的特定实例(此处为 ob
)调用此方法,但它会影响类本身,因此新定义的方法也可用于后续从 X 类创建的任何实例(此处为 ob2
):
ob2 = X.new
ob2.instance_variable_set("@aname", "Mary")
ob2.xyz
如果你不关心对象中数据的封装性,你还可以使用 instance_variable_get
方法获取实例变量的值:
ob2.instance_variable_get( :@aname )
你可以类似地设置和获取常量:
X::const_set( :NUM, 500 )
puts( X::const_get( :NUM ) )
const_get
可以返回常量的值,所以你可以使用此方法获取类名的值,然后附加新方法以从该类创建新对象。这甚至可以通过提示用户输入类名和方法名来为你提供在运行时(runtime)创建对象的方法。通过运行此程序试试这个:
class X
def y
puts( "ymethod" )
end
end
print( "Enter a class name: ") #<= Enter: X
cname = gets().chomp
ob = Object.const_get(cname).new
p( ob )
print( "Enter a method to be called: " ) #<= Enter: y
mname = gets().chomp
ob.method(mname).call