我正在阅读为什么的ruby的辛酸指南,在第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为什么他要在类Creature的元类上调用instance_eval?因为instance_eval向metaclass添加了方法,所以他可以这样做:
def self.metaclass; self; end;还是我错了?有没有更好的解决方案呢?
发布于 2015-10-15 23:18:44
编写_为什么的代码的更简单的方法是
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这里唯一的问题是你会得到一个错误:
private method `define_method' called for metaclass (NoMethodError)私有方法只能通过隐式接收器调用,即问题出在方法调用之前的显式metaclass.。但是如果我们去掉它,隐式接收器(self)就是Creature!那么如何将self更改为不同的对象呢?instance_eval
metaclass.instance_eval do
define_method(a) do |val|
...
end
end因此,这实际上是一种绕过define_method是私有的事实的方式。另一种破解它的方法是使用send
metaclass.send(:define_method, a) do |val|
...
end但现在,所有这些都是完全没有必要的;你可以在元类(AKA singleton类)中定义方法,而不需要绕过私有方法:
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发布于 2015-10-15 18:27:40
如果你这样做了
def self.metaclass; self; end;您将拥有对Creature类的引用。因此,在这种情况下,方法将不会被定义到对象Creature的单个类,而是被定义到类Creature本身(定义到类Creature的实例方法列表中)。该方法
def self.metaclass; class << self; self; end; end是在ruby < 1.9中检索Creature对象的单个类的简单方法。在ruby 1.9+中实现了class << self的快捷方法singleton_class。因此代码可以简化为:
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
endhttps://stackoverflow.com/questions/33145581
复制相似问题