gpt4 book ai didi

ruby - 如何理解class_eval()和instance_eval()的区别?

转载 作者:数据小太阳 更新时间:2023-10-29 06:20:57 24 4
gpt4 key购买 nike

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 应该做什么的直观理解相矛盾。

这些方法之间的真正区别是什么?

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 在模块或类的上下文中评估字符串或 block 。所以下面的代码是等价的:

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

最后 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 - 如何理解class_eval()和instance_eval()的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/900419/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com