我正在阅读为什么对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
为什么他在班级instance_eval
的元类上调用Creature
?由于instance_eval
将方法添加到metaclass
,因此他可以执行此操作:
def self.metaclass; self; end;
或者我错了?还有更优雅的解决方案吗?
答案 0 :(得分:1)
如果你这样做
def self.metaclass; self; end;
您将引用Creature
课程。因此,在这种情况下,方法将不是定义对象的单个类Creature
,而是定义到类Creature
本身(到类Creature
的实例方法列表)。方法
def self.metaclass; class << self; self; end; end
是一种在ruby中检索单个类对象Creature
的简单方法。 1.9。在ruby 1.9+中实现了方法singleton_class
,它是class << self
的捷径。所以代码可以简化为:
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
答案 1 :(得分:1)
编写_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
这里唯一的问题是你得到一个错误:
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单例类)中定义方法:
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