一点点不同 module_eval, instance method, singleton method

这个标题比较怪,实际上很简单,看下列代码
ruby 代码
 
  1. class Test  
  2.     class <<self  
  3.         def add_method_a  
  4.             module_eval %{  
  5.                 def method_a  
  6.                 end  
  7.             }  
  8.         end  
  9.     end  
  10.       
  11.     add_method_a  
  12. end  
  13.   
  14. puts Test.new.respond_to?(:method_a)  
运行结果是打印出 true, 不知道会不会有人和我有一样的疑问,为什么在singleton方法add_method_a中调用module_eval 定义动态方法的结果是为Test类增加了一个instance method而不是一个singleton method。
其实,解释很简单,原因就在于,ruby解释过程中instance method和singleton method处理方式的一点不同:
cpp 代码
 
  1. // singleton method的解释过程    
  2. case NODE_DEFS:    
  3.     VALUE klass;    
  4.     // ... ...    
  5.     klass = rb_singleton_class(recv);    
  6.     // ... ...    
  7.     rb_add_method(klass, node->nd_mid, defn,    
  8.               NOEX_PUBLIC|(body?body->nd_noex&NOEX_UNDEF:0));   
cpp 代码
 
  1. // eval.c    
  2. VALUE ruby_class;    
  3. // ... ...    
  4. // 普通method的处理方式    
  5. case NODE_DEFN:    
  6.     // ... ....    
  7.     rb_add_method(ruby_class, node->nd_mid, defn, noex);   
虽然,两者同样使用rb_add_method添加method,但前者使用局部变量klass,而后者使用的是全局变量ruby_class。
ruby_class 是ruby解释过程中栈信息的一部分,用来记录当前语句(Node)所在的类范围,很明显,例子中module_eval执行时,ruby_class指向的仍然是Test类,因此,运行的结果是我们为Test添加了一个instance method。
对此,我们可以略微加以证明,在上面两处调用rb_add_method的地方加上一条打印信息:
cpp 代码
 
  1. printf("curren class: %s method: %s\n", rb_class2name(ruby_class), rb_id2name(node->nd_mid));    

编译ruby,再次运行样例代码, 结果很清楚的告诉我们,此时ruby_class指向的都是Test类。
写倒这里,我们也顺带证明了一下一个我们常用的小技巧:
ruby 代码
 
  1. // 例如我们自己的"attr_accessor"
  2. module MyAttribute  
  3.     def add_attribute name  
  4.         module_eval %{  
  5.             def #{name}  
  6.                 @#{name}  
  7.             end  
  8.               
  9.             def #{name}= value  
  10.                 @#{name} = value  
  11.             end  
  12.         }  
  13.     end  
  14. end  
  15.   
  16. class TestModule  
  17.     extend MyAttribute  //增加singleton method add_attribute
  18.     add_attribute :my_attr  
  19. end  
  20.   
  21. require 'date'  
  22. t = TestModule.new  
  23. t.my_attr = Date.today  
  24. puts t.my_attr  

可以方便为我们的类添加一些简单实用的小方法 :)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值