pez*_*ser 54 ruby class-method instance-method
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>
Run Code Online (Sandbox Code Playgroud)
只是基于方法的名称,我希望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
在mod的上下文中计算字符串或块.这可以用于向类添加方法.
obj.instance_eval {| | block} => obj
在接收器(obj)的上下文中计算包含Ruby源代码或给定块的字符串.为了设置上下文,在代码执行时将变量self设置为obj,使代码可以访问obj的实例变量.
tom*_*fro 91
正如文档class_eval所述,在Module或Class的上下文中计算字符串或块.所以下面的代码是等价的:
class String
def lowercase
self.downcase
end
end
String.class_eval do
def lowercase
self.downcase
end
end
Run Code Online (Sandbox Code Playgroud)
在每种情况下,String类都已重新打开并定义了一个新方法.该方法适用于所有类的实例,因此:
"This Is Confusing".lowercase
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
=> "the smiths on charlie's bus"
Run Code Online (Sandbox Code Playgroud)
class_eval与简单地重新开课相比,它有许多优点.首先,您可以轻松地在变量上调用它,并且很清楚您的意图是什么.另一个优点是,如果该类不存在,它将失败.因此,下面的示例将失败,因为Array拼写不正确.如果只是重新打开该类,它将成功(并且Aray将定义一个新的不正确的类):
Aray.class_eval do
include MyAmazingArrayExtensions
end
Run Code Online (Sandbox Code Playgroud)
最后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
Run Code Online (Sandbox Code Playgroud)
因此instance_eval,该方法仅针对字符串的单个实例进行定义.
那么为什么instance_eval在Class定义类方法呢?
正如"This Is Confusing"和"The Smiths on Charlie's Bus"都String实例Array,String,Hash和所有其他类本身的实例Class.您可以通过调用#class它们来检查:
"This Is Confusing".class
=> String
String.class
=> Class
Run Code Online (Sandbox Code Playgroud)
所以当我们调用instance_eval它时,它在类上和在任何其他对象上一样.如果我们使用instance_eval在类上定义方法,它将仅为该类实例定义一个方法,而不是所有类.我们可能将该方法称为类方法,但它只是该特定类的实例方法.
jed*_*iah 17
另一个答案是正确的,但请允许我深入一点.
Ruby有许多不同的范围; 虽然详细的正式文件似乎缺乏,但根据维基百科的说法有六个.毫无疑问,这个问题涉及的范围是实例和类.
当前实例范围由值的定义self.所有非限定方法调用都被调度到当前实例,对实例变量(看起来像@this)的任何引用都是如此.
但是,def不是方法调用.创建的方法的目标def是当前的类(或模块),可以在其中找到Module.nesting[0].
让我们看看两种不同的eval风格如何影响这些范围:
String.class_eval { [self, Module.nesting[0]] }
=> [String, String]
String.instance_eval { [self, Module.nesting[0]] }
=> [String, #<Class:String>]
在这两种情况下,实例范围都是调用*_eval的对象.
因为class_eval,类范围也成为目标对象,因此def为该类/模块创建实例方法.
因为instance_eval,类范围成为目标对象的单例类(也称为元类,本征类).在单个类上为对象创建的实例方法成为该对象的单例方法.类或模块的单例方法通常(并且有些不准确)称为类方法.
类范围也用于解析常量.类变量(@@these @@things)使用类作用域解析,但在搜索模块嵌套链时会跳过单例类.我发现在单例类中访问类变量的唯一方法是使用class_variable_get/set.
我觉得你弄错了.class_eval在类中添加方法,因此所有实例都将具有该方法.instance_eval将该方法仅添加到一个特定对象.
foo = Foo.new
foo.instance_eval do
def instance_bar
"instance_bar"
end
end
foo.instance_bar #=> "instance_bar"
baz = Foo.new
baz.instance_bar #=> undefined method
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11775 次 |
| 最近记录: |