如何理解class_eval()和instance_eval()之间的区别?

2024-03-15

Foo = Class.new
Foo.class_eval do
  def class_bar
    "class_bar"
  end
end
Foo.instance_eval do
  def instance_bar
    "instance_bar"
  end
end
Foo.class_bar       #=> undefined method ‘class_bar’ for Foo:Class
Foo.new.class_bar   #=> "class_bar"
Foo.instance_bar       #=> "instance_bar"
Foo.new.instance_bar   #=> undefined method ‘instance_bar’ for #<Foo:0x7dce8>

仅根据方法的名称,我希望 class_eval 允许您向 Foo 添加类方法,而 instance_eval 允许您向 Foo 添加实例方法。但他们似乎反其道而行之.

在上面的示例中,如果您在 Foo 类上调用 class_bar,则会收到未定义方法错误;如果您在 Foo.new 返回的实例上调用 instance_bar,也会收到未定义方法错误。这两个错误似乎都与对 class_eval 和 instance_eval 应该做什么的直观理解相矛盾。

这些方法之间到底有什么区别?

文档用于类评估 http://www.ruby-doc.org/core-1.8.6/classes/Module.html#M001673:

mod.class_eval(字符串[文件名[ lineno]]) => obj

评估字符串或块 模组的上下文。这可以用来 向类添加方法。

文档用于:

obj.instance_eval {| |块 } => obj

计算包含 Ruby 的字符串 源代码或给定的块, 在接收者的上下文中 (对象)。为了设置上下文, 变量 self 设置为 obj 而 代码正在执行,给出代码 访问 obj 的实例变量。


正如文档所说,class_eval在模块或类的上下文中评估字符串或块。所以下面的代码是等价的:

class String
  def lowercase
    self.downcase
  end
end

String.class_eval do
  def lowercase
    self.downcase
  end
end

在每种情况下,String 类都被重新打开并定义了一个新方法。该方法可在该类的所有实例中使用,因此:

"This Is Confusing".lowercase 
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
=> "the smiths on charlie's bus"

class_eval与简单地重新开课相比,有很多优点。首先,您可以轻松地在变量上调用它,并且很清楚您的意图是什么。另一个优点是,如果该类不存在,它将失败。所以下面的例子将会失败Array拼写错误。如果简单地重新开放该课程,它就会成功(并且会出现一个新的错误Aray类将被定义):

Aray.class_eval do
  include MyAmazingArrayExtensions
end

Finally class_eval可以接受一个字符串,如果你正在做一些更邪恶的事情,这会很有用......

instance_eval另一方面,针对单个对象实例评估代码:

confusing = "This Is Confusing"
confusing.instance_eval do
  def lowercase
    self.downcase
  end
end   

confusing.lowercase
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
NoMethodError: undefined method ‘lowercase’ for "The Smiths on Charlie's Bus":String

So with instance_eval,该方法仅为字符串的单个实例定义。

那么为什么instance_eval on a Class定义类方法?

Just as "This Is Confusing" and "The Smiths on Charlie's Bus"都是String实例,Array, String, Hash所有其他类本身都是Class。您可以通过致电来检查这一点#class在他们:

"This Is Confusing".class
=> String

String.class
=> Class

所以当我们打电话时instance_eval它对类的作用与对任何其他对象的作用相同。如果我们使用instance_eval要在类上定义方法,它将仅为该类的实例而不是所有类定义方法。我们可以将该方法称为类方法,但它只是该特定类的实例方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何理解class_eval()和instance_eval()之间的区别? 的相关文章

随机推荐