Reflection对我来说是个新概念,其含义很简单,我们可以使用支持Reflection的语言写一个能够查看自身信息的程序。第一次接触Reflection,感觉它对于学习语言非常有帮助,我们可以写一些程序来了解语言和运行时系统,不用再去构造“shape, circle, rectangle, draw”之类书卷气十足的例子。
Ruby的Reflection支持从对象查看类,查看类的基类,程序中有那些全局变量、局部变量等等。查看对象的类型很简单,如:
$ irb irb(main):001:0> 5.class => Fixnum
第一次发现程序可以这样写时觉得很惊奇,其实并不难理解,Ruby的立即数的类型是一个类,而不像其它语言那样是一种内建类型,所以,5实际上是一个对象,因此我们可以调用该对象的类型支持的方法,class
就是其中之一,它返回该对象的类型。可以看出,5的类型是Fixnum
。让我们看看文档里是怎么介绍Fixnum
的
$ ri -T Fixnum ------------------------------------------------ Class: Fixnum < Integer A +Fixnum+ holds +Integer+ values that can be represented in a native machine word (minus 1 bit). If any operation on a +Fixnum+ exceeds this range, the value is automatically converted to a +Bignum+.
看来还有一种类型叫Bignum
$ ri -T Bignum ------------------------------------------------ Class: Bignum < Integer Bignum objects hold integers outside the range of Fixnum. Bignum objects are created automatically when integer calculations would otherwise overflow a Fixnum. When a calculation involving Bignum objects returns a result that will fit in a Fixnum, the result is automatically converted.
似乎没有什么特别值得关注的内容了,让我们回到前面的例子。需要特别指出的是,class
的返回值并不是字符串,而是一个类对象,比如我们可以调用superclass
查看Fixnum
的基类是什么。
irb(main):002:0> 5.class.superclass => Integer
如果我们留意前面的文档,会发现第一行有Fixnum < Integer
和Bignum < Integer
这样的表述,看来<是用来描述子类与基类关系的符号。我们的好奇心也被激起了,到底这是怎样一幅类的层次图呢?
$ cat class.rb #!/usr/bin/ruby c = 5.class while c s = c.superclass if s then print c, " < " else print c end c = s end puts $ ruby class.rb Fixnum < Integer < Numeric < Object
噢,我们找到了Object
$ ri -T Object ---------------------------------------------------------- Class: Object +Object+ is the parent class of all classes in Ruby. Its methods are therefore available to all objects unless explicitly overridden.
单根?!这让我想起了使用Delphi
的日子……
也许把c
作为print
的参数让人感觉它是个字符串,其实不然,只不过Ruby在这里做了一次自动的类型转换。下面的程序统计出当前共有352个类,如果想知道都有哪些,可以用puts o
代替1。
irb(main):003:0> ObjectSpace.each_object(Class) {|o| 1 } => 352
使用ri
可以详细了解ObjectSpace
,ObjectSpace#each_object
和Class
的详细解释。简单的说,它提供了一种方式遍历所有的类,并对每个类调用花括号之内的程序。两条竖线之间的o代表当前正被遍历到的类,这里的o
有点像函数的参数,我们也可以使用其它变量名。下面的程序显示了一个更加完全的类层次:
$ cat objectspace.rb #!/usr/bin/ruby c = 5.class level = 1 while c print "#{level}: #{c}" ObjectSpace.each_object(Class) do |o| print " #{o}" if c.superclass == o.superclass and c != o end puts c = c.superclass level += 1 end $ ruby objectspace.rb 1: Fixnum Bignum 2: Integer Float 3: Numeric Binding UnboundMethod Method Proc Process::Status Time Dir File::Stat IO Range MatchData Regexp Struct Hash Array ThreadGroup Continuation Thread Exception String FalseClass TrueClass Data Symbol NilClass Module 4: Object
这里的类并不多,把每个类的文档看一遍,不算是过分的要求。
前面的例子都是从5开始的,我们还可以从另一个方向——字符串——做同样的事情,还是从”hello, world”开始吧
irb(main):004:0> "hello, world".class => String
接下来的部分就由大家自己完成吧。
好,我们再回到5。
irb(main):005:0> 5.methods
大家可以尝试一下,看看我们都可以对5做什么?不过返回的结果有点乱,不太好看,让我们整理一下。比如看有哪些操作符:
irb(main):006:0> 5.methods.grep(/^W*$/)
如果觉得结果太多也可以选择一部分:
irb(main):007:0> 5.methods.grep(/^w*$/)[0..5] => ["upto", "div", "object_id", "times", "singleton_methods", "taint"]
还可以先排序再筛选
irb(main):008:0> 5.methods.sort.grep(/^w*$/)[10..15] => ["divmod", "downto", "dup", "extend", "floor", "freeze"]
sort
和grep
都是基于数组的操作,与Perl中的类似。
其它的如respond_to?
,kind_of?
,instance_of?
,以及Kernel#global_variables
和Kernel#local_variables
等Reflection功能就留给大家自己玩吧。