Ruby 中的 class_eval 和 module_eval 方法

yufei       6 年, 3 月 前       1380

日常使用 Ruby 时,在 「 运行时 」 ( on-the-fly ) 向类或模块添加方法或属性是司空见惯的事情,例如 activerecordactivesupportrankrack 等等。

为什么这么司空见惯呢 ?

因为 Ruby 提供了 Module#class_evalModule#module_eval 两个方法可以在 「 运行时 」 注入属性和方法。

你接下来可能会有疑问,既然两个方法都在 Module 上,那它们的区别是什么呢 ?

class_eval

在 Ruby 中,当我们想要向 添加方法时,我们可以使用 Module#class_eval 方法。该方法接受 字符串 或 块 ( block ) 作为参数

array_second = <<-RUBY
def second
  self[1]
end
RUBY
Array.class_eval(array_second)
String.class_eval do
  def /(delimiter)
    split(delimiter)
  end
end
$> [1,2,3].second
 => 2
$> "1,2,3" / ','
  => ["1", "2", "3"]

上面的代码中,调用 Array.class_eval(array_second) 会将 array_second 方法添加到 Array 的所有实例中。

class_eval 的机制很简单,就会在类的上下文运行 class_eval 中的字符串来达到此目的。

而向 String#class_eval 传递一个块 ( block ) 时,则会在 String 的上下文中运行参数块的内容。上面的代码,我们添加了 String#/(delimiter) 方法,这是一个运算符 / ,对 String 的所有实例都可用。

提示,如果你对 <<-RUBY 语法不熟悉的话,可以阅读 Ruby heredoc 中不使用字符串插值 来了解这种语法

module_eval

Module#module_eval 方法和 Module#class_eval 方法类似,只不过它是向 模块 module 添加运行时方法而已。

module Commentable
  def add_comment(comment)
    self.comments << comment
  end
  def comments
    @comments ||= []
  end
end
Commentable.module_eval do
  def comment_count
    comments.count
  end
end
class Post
  include Commentable
end
$> post = Post.new
 => #<Post:0x00007fd9ac0238b0> 
$> post.add_comment("Very nice !")
 => ["Very nice !"] 
$> post.comment_count
 => 1

一套核心,两套皮肤

class_eval 方法用于向类添加运行时属性或方法。而 module_eval 则在运行时向模块添加属性或方法。

但这只是表面的情况。

在内部,class_eval 其实就是 module_eval 的别名

我们只要看一下 Ruby 的源代码 ruby/vm_eval.c 就能证明我们刚刚所说的

rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -1);    rb_define_method(rb_cModule, "class_eval",  rb_mod_module_eval, -1);

从源代码中可以看到, module_evalclass_eval 都指向了同样的 C 语言函数 rb_mod_module_eval()

由于 C 语言函数的名称为 module_eval ,因此,我们可以说 class_evalmodule_eval 的别名

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.