当它看起来如此相似时,为什么本征类不等同于self.class?

时间:2009-10-27 13:31:14

标签: ruby language-features eigenclass

我错过了某个地方的备忘录,我希望你能向我解释这个。

为什么对象的本征类与self.class不同?

class Foo
  def initialize(symbol)
    eigenclass = class << self
      self
    end
    eigenclass.class_eval do
      attr_accessor symbol
    end
  end
end

将特征类与class.self等同起来的逻辑逻辑非常简单:

class << self是一种声明类方法的方法,而不是实例方法。这是def Foo.bar的捷径。

因此,在对类引用的引用中,返回self应该与self.class相同。这是因为class << self会将self设置为Foo.class以定义类方法/属性。

我只是困惑吗?或者,这是Ruby元编程的一个偷偷摸摸的技巧吗?

3 个答案:

答案 0 :(得分:120)

class << self不仅仅是一种声明类方法的方法(尽管可以这样使用)。可能你已经看到了一些用法:

class Foo
  class << self
    def a
      print "I could also have been defined as def Foo.a."
    end
  end
end

这有效,相当于def Foo.a,但它的工作方式有点微妙。秘诀是self在该上下文中引用了对象Foo,其类是Class的唯一匿名子类。该子类称为Foo本征类。因此,def aa的本征类中创建了一个名为Foo的新方法,可通过常规方法调用语法访问:Foo.a

现在让我们看一个不同的例子:

str = "abc"
other_str = "def"

class << str
  def frob
    return self + "d"
  end
end

print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str

此示例与最后一个示例相同,但最初可能很难说。 frob的定义不是String类,而是str的本征类,String的唯一匿名子类。因此,str具有frob方法,但String的实例通常不会。我们也可以覆盖String的方法(在某些棘手的测试场景中非常有用)。

现在我们已经准备好了解您的原始示例。在Foo的初始化方法中,self不是指Foo类,而是指Foo的某个特定实例。它的本征类是Foo的子类,但它不是Foo;它不可能,否则我们在第二个例子中看到的技巧无法奏效。所以继续你的例子:

f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)

f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.

希望这有帮助。

答案 1 :(得分:45)

最简单的答案:无法实例化本征类。

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class

答案 2 :(得分:11)

Yehuda Katz在解释“Metaprogramming in Ruby: It's All About the Self”中的微妙之处方面做得非常出色