反射和元编程
匿名Class对象
如果把一个Class对象,赋值给一个常量,那么这个常量就是一个类名。因此在Ruby中每一个类都是一个Class常量对象。因此Object.class = Class
例子:
C = Class.new # A new class with no body, assigned to a constant
c = C.new # Create an instance of the class
c.class.to_s # => "C": constant name becomes class name
运行时信息
类型信息
注意:o是一个对象,c是一个类
1.o.class
2.c.superclass
3.o.instance_of? c#相当于o.class == c
4.o.is_a? c 或者 o.kind_of? 或者 c === o。c#测试o的父类或者类型是否为c,或者o的类型是否包含c
5.只要创建了模块或者类,接下来就可以用开放类或者模块来添加各种东西。
模块相关的信息
module A; end # Empty module
module B; include A; end; # Module B includes A
class C; include B; end; # Class C includes module B
C < B # => true: C includes B
B < A # => true: B includes A
C < A # => true
Fixnum < Integer # => true: all fixnums are integers
Integer <Comparable # => true: integers are comparable
Integer < Fixnum # => false: not all integers are fixnums
String < Numeric # => nil: strings are not numbers
A.ancestors # => [A]
B.ancestors # => [B, A]
C.ancestors # => [C, B, A, Object, Kernel]
String.ancestors # => [String, Enumerable, Comparable, Object, Kernel]
# Note: in Ruby 1.9 String is no longer Enumerable
C.include?(B) # => true
C.include?(A) # => true
B.include?(A) # => true
A.include?(A) # => false
A.include?(B) # => false
A.included_modules # => []
B.included_modules # => [A]
C.included_modules # => [B, A, Kernel]
extend
Object的extend可以自动把模块的实例方法变成单例方法
module Greeter; def hi; "hello"; end; end # A silly module
s = "string object"
s.extend(Greeter) # Add hi as a singleton method to s
s.hi # => "hello"
String.extend(Greeter) # Add hi as a class method of String
String.hi # => "hello"
动态创建模块
每一个模块都是一个Nodule类的一个实例。因此可以通过创建Module常量来创建模块。
M = Module.new
或者用new的块语法
Fed = Module.new do
def meth1
"hello"
end
def meth2
"bye"
end
end
动态创建类
C = Class.new
D = Class.new(C) {
include M
}
对象信息
object_id得到对象ID
属性
class Point
def initialize(x,y); @x,@y = x,y; end # Define instance variables
@@classvar = 1 # Define a class variable
ORIGIN = Point.new(0,0) # Define a constant
end
Point::ORIGIN.instance_variables # => ["@y", "@x"]
Point.class_variables # => ["@@classvar"]
Point.constants # => ["ORIGIN"]
动态添加属性
o = Object.new
o.instance_variable_set(:@x, 0) # Note required @ prefix
o.instance_variable_get(:@x) # => 0
o.instance_variable_defined?(:@x) # => true
Object.class_variable_set(:@@x, 1) # Private in Ruby 1.8
Object.class_variable_get(:@@x) # Private in Ruby 1.8
Object.class_variable_defined?(:@@x) # => true; Ruby 1.9 and later
Math.const_set(:EPI, Math::E*Math::PI)
Math.const_get(:EPI) # => 8.53973422267357
Math.const_defined? :EPI # => true
注意:这儿易受静态语言的影响,而认为这些变量或者常量是已经存在于类中的。事实上,在Ruby中,当变量或者常量在第一次出现时,会加入到类体中,动态添加的时候这种行为显得更加魔幻。但是由于一个属性必须有个访问方法,因此对于外界来说也是一种提示。
删除
o.instance_eval { remove_instance_variable :@x }
String.class_eval { remove_class_variable(:@@x) }
Math.send :remove_const, :EPI # Use send to invoke private method
注意:因为remove方法是私有的,因此只能用eval和send来包装调用
方法
o = "a string"
o.methods # => [ names of all public methods ]
o.public_methods # => the same thing
o.public_methods(false) # 不包含继承的公有方法
o.protected_methods # => []: there aren't any
o.private_methods # => array of all private methods
o.private_methods(false) # Exclude inherited private methods
def o.single; 1; end # Define a singleton method
o.singleton_methods # => ["single"] (or [:single] in 1.9)
在Module中定义的类方法
String.instance_methods == "s".public_methods # => true
String.instance_methods(false) == "s".public_methods(false) # => true
String.public_instance_methods == String.instance_methods # => true
String.protected_instance_methods # => []
String.private_instance_methods(false) # => ["initialize_copy", "initialize"]
Math.singleton_methods # => ["acos", "log10", "atan2", ... ]
参见: Method (相同)
测试特定方法
String.public_method_defined? :reverse # => true
String.protected_method_defined? :reverse # => false
String.private_method_defined? :initialize # => true
String.method_defined? :upcase! # => true
对于对象来说
o.respond_to? name 测试o是否有一个方法,另一个参数如果为true,那么也可以测试private方法
动态调用方法
1,得到一个method来调用
2,用send或者public_send来发送消息
3,类和方法都可以调用,谁调用就在谁上面执行方法
"hello".send :upcase # => "HELLO": invoke an instance method
Math.send(:sin, Math::PI/2) # => 1.0: invoke a class method
"hello".public_send :puts, "world" # raises NoMethodError
4,模块也可以调用这个方法
def define_methods
shared = 0
Kernel.send :define_method, :counter do
shared
end
Kernel.send :define_method, :inc do |x|
shared += x
end
end
动态添加方法
1.添加实例方法
def add_method(c, m, &b)
c.class_eval {
define_method(m, &b)
}
end
add_method(String, :greet) { "Hello, " + self }
"world".greet # => "Hello, world"
注意的define_method是kernel的私有的方法,因此要用class_eval来包装他的调用
2.添加类方法
String.define_singleton_method(:greet) {|name| "Hello, " + name }
动态定义中的实例属性
在Ruby中,一个类调用instance_eval和define_method都是为了创建实例方法(这两个方法都是由高层类和模块调用的,因此类和对象中都会有这两个方法)。既然是实例方法,那么就会调用实例属性和实例方法。这在Java中是不可思议的,因为Java中所有的类方法都不可以调用实例方法。但是在Ruby中这些方法仅仅会把得到的块传递给类,而并不会检测其中的内容,也不会在类定义期间执行,因此不用担心是否会有实例属性未初始化的情况。这种做法就像C语言中的宏替换。
重命名方法
def backup(c, m, prefix="original")
n = :"#{prefix}_#{m}" # Compute the alias
c.class_eval { # Because alias_method is private
alias_method n, m # Make n an alias for m
}
end
鉴于这些方法都是Module中的私有方法,因此必须用class_eval来包装调用
动态删除
remove_method和undef_method
它们的不同之处是:undef_method会删除继承的方法,也就是说删除之后就没有了这个方法;remove_method仅仅删除子类的定义,父类的方法大这是被继承。
改变可见性
1.可以简单的用开类来实现
String.class_eval { private :reverse }
2.Module中的方法
禁止动态变化
一个类要想禁止动态变化,可以简单的调用freeze方法冻结
eval
eval "x + 1" # => 2
1.eval可以执行任何有效的Ruby语句,这样甚至可以用生成String的方式来处理代码
2.eval好用,但是容易滥用,因此要注意用其他的特性来代替它
3.eval可能不安全
Binding类
每一个的属性都一个特定的上下文,一般而言是一个对象。这样的状态用Binding对象来表示。Kernel.binding可以得到一个Binding对象(注意这个方法是私有的,因此要想办法变成共有)。接下来就可以用eval来的得到属性的值。
class Object
def bindings
binding # This is the predefined Kernel method
end
end
class Test
def initialize(x); @x = x; end
end
t = Test.new(10)
eval("@x", t.bindings) # => 10
或者在Ruby1.9中。
t.bindings.eval("@a")
instance_eval和class_eval
1.instance_eval和class_eval是在特定的类和对象上执行代码
o.instance_eval("@x") # Return the value of o's instance variable @x
String.class_eval("def len; size; end")
String.class_eval("alias len size")
String.instance_eval("def empty; ''; end")
另外可以用快语法
o.instance_eval { @x }
String.class_eval {
def len
size
end
}
String.class_eval { alias len size }
String.instance_eval { def empty; ""; end }
2.要注意的是instance_eval定义在BasicObject中,而Module中定义了class_eval,同时不是每一对象都是一个Module类实例,而每一个Class都是一个Module(Class->Module->Object)。因此在对象上只能调用instance_eval,而类上可以调用instance_eval和class_eval,当使用这种方法定义方法是,差别是instance_eval定义了一个单例方法(即使没有用前缀),这意味着对类来说,定义了一个类方法,对对象来说定义了一个对象的特有方法。另一方面,class_eval定义了实例方法,这对于对象也无意义。
触发方法
触发方法:也叫钩子,是在模块,类或者对象在被执行一个操作时触发的方法。他门大部分的名字以ed结尾并且都是单例方法。另外,这些方法的执行时机是在创建对象时执行。
inherited
def Object.inherited(c)
puts "class #{c} < #{self}"
end
其中inherited方法在父类中,它的参数c是子类。
included
module Final # A class that includes Final can't be subclassed
def self.included(c) # When included in class c
c.instance_eval do # Define a class method of c
def inherited(sub) # To detect subclasses
raise Exception, # And abort with an exception
"Attempt to create subclass #{sub} of Final class #{self}"
end
end
end
end
extended
实例方法
method_added
def String.method_added(name)
puts "New instance method #{name} added to String"
end
这个方法可以定义在任何模块或者类上,用于在类中添加实例方法时调用。
method_removed
method_undefined
单例方法
如果在定义的时候是类方法,那么就是在类方法被加入时被调用。反之,如果是实例方法,那么就是在对象加入单例方法时被调用。
singleton_method_removed
singleton_method_undefined
singleton_method_added
Module.const_missing
const_missing是一个类方法,用来在常量得不到的情况下处理。
例如:
module Unicode
def self.const_missing(name)
if name.to_s =~ /^U([0-9a-fA-F]{4,5}|10[0-9a-fA-F]{4})$/
codepoint = $1.to_i(16)
utf8 = [codepoint].pack("U")
utf8.freeze
const_set(name, utf8)
else
raise NameError, "Uninitialized constant: Unicode::#{name}"
end
end
end
这种情况下,当在常量不存在的情况下或用const_set来在产生一个常量。这种方法叫做延迟初始化。
BasicObject.method_messing
def method_missing(*args, &block)
m = args.shift
begin
arglist = args.map {|a| a.inspect}.join(', ')
@trace << "Invoking: #{@n}.#{m}(#{arglist}) at #{caller[0]}\n"
r = @o.send m, *args, &block
@trace << "Returning: #{r.inspect} from #{@n}.#{m} to #{caller[0]}\n"
r
rescue Exception => e
@trace << "Raising: #{e.class}:#{e} from #{@n}.#{m}\n"
raise
end
end
1.method_missing是一个实例方法
2.method_missing的第一个参数中的第一个元素是方法名,其他的都是传递给方法的参数。第二个参数是块。
3.这个实现跟踪方法调用。
虚拟机
ObjectSpace
each_object
ObjectSpace.each_object(Class) {|c| puts c }
可以看到这个方法的第一个方法指定类型,第二个块用来处理对象
_id2ref
这个方法和Object.object_id相反,通过一个对象地址来得到对象
define_finalizer
这个方法向一个对象注册一个在垃圾收集时执行的块。undefine_finalizer则删掉所有的块。
include ObjectSpace
a = "A"
b = "B"
c = "C"
define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" })
define_finalizer(a, proc {|id| puts "Finalizer two on #{id}" })
define_finalizer(b, proc {|id| puts "Finalizer three on #{id}" })
garbage_collect
强制垃圾收集
弱引用对象
# foo = Object.new
# foo = Object.new
# p foo.to_s # original's class
# foo = WeakRef.new(foo)
# p foo.to_s # should be same class
# ObjectSpace.garbage_collect
# p foo.to_s # should raise exception (recycled)
GC
enable
disable
start
空白类
在Ruby1.9中的提供了一个空白类中仅仅提供了非常少量的方法,这样在幽灵方法中和动态定义方法中就可以避免名称的冲突。