`
azure_sky
  • 浏览: 12775 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

一点点不同 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  

可以方便为我们的类添加一些简单实用的小方法 :)
分享到:
评论

相关推荐

    ruby metaprograming

    def self.method_missing(name, *args) puts "Called method: #{name}" end my_object = MyClass.new my_object.some_method # 输出: "Called method: some_method" ``` ##### 2. eval `eval`函数可以用来执行一...

    ruby设计模式的代码有好多

    Ruby的元编程能力,如`class_eval`、`instance_eval`和`define_method`等方法,使得动态修改类和对象的行为变得容易,这在实现某些设计模式时特别有用。例如,元编程可以用来在运行时添加方法,实现动态代理或者创建...

    设计模式

    在Ruby中,可以通过模块的extend方法和instance_eval方法实现单例模式,比如: ```ruby class Singleton class attr_accessor :instance end def initialize @state = "I'm the only one!" end def self....

    Ruby元编程技术详解(Ruby Metaprogramming techniques)

    10. `Module#define_method` 和 `Module#method_missing`: `define_method`用于在运行时定义实例方法,而`method_missing`是一个回调方法,当尝试调用不存在的方法时会被触发,这可以用来实现自定义错误处理或动态...

    lookingForObjectByDesignPatternInRuby

    在Ruby中,可以使用`singleton_class`方法或`Module#class_eval`配合`instance`来实现单例模式。 2. **工厂模式(Factory)**:创建对象时,隐藏具体创建过程,提高灵活性。Ruby没有像Java那样的静态方法限制,因此...

Global site tag (gtag.js) - Google Analytics