Ruby学习笔记之反射和元编程

反射和元编程


匿名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中的提供了一个空白类中仅仅提供了非常少量的方法,这样在幽灵方法中和动态定义方法中就可以避免名称的冲突。

DSL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值