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>

仅根据方法的名称,I would expect class_eval to allow you to add a class method to Foo and instance_eval to allow you to add an instance method to Foo. But they seem to do the opposite.

在上面的例子中,如果在Foo类上调用class_-bar,就会得到一个未定义的方法错误,如果在Foo返回的实例上调用instance_-bar.新的,你也会得到一个未定义的方法错误.这两个错误似乎都与class_eval和instance_eval应该做什么的直观理解相矛盾.

What is really the difference between these methods?

class_eval人的文件:

mod.class_eval(string [, filename [, lineno]]) => obj

Evaluates the string or block in the context of mod. This can be used to add methods to a class.

instance_eval人的文件:

obj.instance_eval {| | block } => obj

Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj’s instance variables.

推荐答案

正如文档所述,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.class_eval do
  include MyAmazingArrayExtensions
end

最后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

因此,对于instance_eval,该方法仅为字符串的单个实例定义.

那么为什么Class上的instance_eval定义了类方法呢?

正如"This Is Confusing""The Smiths on Charlie's Bus"都是String个实例一样,ArrayStringHash和所有其他类本身都是Class的实例.您可以拨打#class查询:

"This Is Confusing".class
=> String

String.class
=> Class

因此,当我们调用instance_eval时,它对类的作用与对任何其他对象的作用相同.如果我们使用instance_eval来定义一个类的方法,它将只为该类的实例定义一个方法,而不是为所有类定义一个方法.我们可以将该方法称为类方法,但它只是该特定类的实例方法.

Ruby相关问答推荐

在Glade创建的UI中以编程方式填充GtkTreeView

Rails 7 - 访问连接模型生成新查询

当数据包含换行符时,Ruby PKCS7 无法验证

这是 ruby​​ Regexp 中的错误吗?如何在不使用超时的情况下防止正则表达式匹配中的无限循环?

如何使用 Ruby 的 optparse 解析没有名称的参数

如何在Ruby中从字符串中go 除前导和尾随引号

rbenv 没有显示可用的 ruby​​ 版本

在 Ruby 中,获取第一个块返回 true 的可枚举元素的最快方法是什么?

Ruby - net/http - 重定向

Ruby:从字节创建一个字符串

Ruby:获取不带扩展名的文件名

Ruby 中的 main是什么?

有没有办法在 RSpec 中取消存根?

ActionController::UnknownFormat

require File.expand_path(..., __FILE__) 是最佳实践吗?

从 Ruby 中的字符串创建不区分大小写的正则表达式

理解 Ruby 中的私有方法

if语句末尾带有then有什么区别?

将 lambda 作为块传递

Capybara:按值而不是文本 Select 选项