[英]Using `instance_eval`
I am reading why's poignant guide to ruby, and in chapter 6, he used this code: 我正在阅读为什么是红宝石的辛辣指南,在第6章中,他使用了以下代码:
class Creature
# Get a metaclass for this class
def self.metaclass; class << self; self; end; end
...
def self.traits( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
@traits ||= {}
@traits[a] = val
end
end
end
end
end
Why is he calling instance_eval
on metaclass of class Creature
? 为什么他在
Creature
类的元类上调用instance_eval
? Since instance_eval
adds methods to metaclass
, he can just do this: 由于
instance_eval
将方法添加到metaclass
,他可以这样做:
def self.metaclass; self; end;
Or am I wrong? 还是我错了? Are there any more elegant solutions to this?
还有其他更优雅的解决方案吗?
If you do 如果你这样做
def self.metaclass; self; end;
you will have a reference to Creature
class. 您将参考
Creature
类。 So, in that case, the methods will be defined not to the singleton class of object Creature
but to the class Creature
itself(to the list of instance methods of class Creature
). 所以,在这种情况下,该方法将被定义为不将单例类对象的
Creature
,但对类Creature
本身(到类的实例方法列表Creature
)。 The method 方法
def self.metaclass; class << self; self; end; end
is a simple way to retrieve singleton class of object Creature
in ruby < 1.9. 是在<1.9以下的ruby中检索对象
Creature
单例类的简单方法。 In ruby 1.9+ was implemented method singleton_class
which is shortcut of class << self
. 在ruby 1.9+中实现了方法
singleton_class
,这是class << self
快捷方式。 So that code can be simplified as: 这样该代码可以简化为:
class Creature
...
def self.traits( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
singleton_class.instance_eval do
define_method( a ) do |val|
@traits ||= {}
@traits[a] = val
end
end
end
end
end
The simpler way to write _why's code would be just 编写_why代码的更简单方法是
def self.traits( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
metaclass.define_method(a) do |val|
@traits ||= {}
@traits[a] = val
end
end
end
The only problem here is that you get an error: 唯一的问题是出现错误:
private method `define_method' called for metaclass (NoMethodError)
A private method can only be called with an implicit receiver ie the problem is the explicit metaclass.
只能使用隐式接收方调用私有方法,即问题是显式
metaclass.
before the method call. 在方法调用之前。 But if we remove that, the implicit receiver (
self
) is Creature
! 但是,如果我们删除它,隐式接收者(
self
)就是Creature
! So how do we change self
to a different object? 那么我们如何将
self
变成另一个对象呢? instance_eval
: instance_eval
:
metaclass.instance_eval do
define_method(a) do |val|
...
end
end
So it's really just a way of bypassing the fact that define_method
is private. 因此,这实际上只是绕过
define_method
是私有的事实的一种方式。 Another way to hack it would be to use send
破解的另一种方法是使用
send
metaclass.send(:define_method, a) do |val|
...
end
But these days all of that is totally unnecessary; 但是如今,这一切都是完全不必要的。 you are allowed to define methods in the metaclass (AKA singleton class) without hacking around private methods:
您可以在元类(AKA单例类)中定义方法,而不必乱砍私有方法:
def self.traits( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
define_singleton_method(a) do |val|
@traits ||= {}
@traits[a] = val
end
end
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.