首页 > 代码库 > ruby的def 和 define_method的细致差别

ruby的def 和 define_method的细致差别

        ruby语言中,class_eval和instance_eval的具体差别比较明显, class_eval针对的是一个Class的对象,然后在此对象中可以定义instance方法。而instance_eval是针对某个对象,打开的class是 eigenclass或者singleton class。

class A 

end

A.instance_eval do

     def hello

        puts "hello"

     end

end

A.class_eval do

     def world

         puts "world"

     end

end

此时,我们定义的是A的class方法,也就是说我们在A.eigenclass中定义了一个实例方法(instance method)"hello".

A.hello   # hello

A.world  # NoMethodError: undefined method `world‘ for A:Class

A.new.world # hello


到这一点为止,大家都感觉比较好明白。

现在下面的问题来了,

A.instance_eval do

   define_method(:t1) do

       puts "t1"

   end

end

按照很多人说法,define_method和def机制类似的,只是def更高效,define_method是动态定义方法而已,并且define_method可以使用局部的变量。

所以,我们从上面分析,方法:t1 应该还是A的eigenclass的实例方法,或者说A的类方法。

A.t1  应该输出 "t1"

但实际是:

NoMethodError: undefined method `t1‘ for A:Class

A.new.t1 # t1

所以在instance_eval下,define_method 和 class_eval 是一致的,都是定义了A得实例方法。

而如果我们这样写,

class <<A

    define_method(:t2) do

       puts "t2"

    end

end

A.t2 # t2

上面这种方法确实是在A.eigenclass中定义了实例方法 t2。

在这篇文章开头,我们说过,A.instance_eval 确实打开的时eigenclass, 为什么对def是正确地,而对define_method是不对的呢? 而在class << A 这种显示打开eigenclass时, define_method也是符合逻辑的。

或许是为了方便写meta方法,做了这种有意的调整。

klass.instance_evaldo

      method_object = instance_method(method)

     define_method(method)do |*args, &block|
                puts "==> calling #{method} with #{args.inspect}"

               result = method_object.bind(self).call(*args, &block)

              puts "<== #{method} returned #{result.inspect}"

              result

      end

end 


在上面这段代码里,对klass做了定义方法,而大部分这种写代码而言,动态生成方法都是想生成实例方法,而不是klass的类方法,所以,define_method在这个时候,做了特殊的变化,从而把我们先前总结的规律给引入了一个特殊的例外。如果不引入特殊的例外,这时,必须再加一个包装层, klass.class_eval do   ....  end 来做处理。


ruby的def 和 define_method的细致差别