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

Why isn't the eigenclass equivalent to self.class, when it looks so similar?

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

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

1
2
3
4
5
6
7
8
9
10
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元编程的偷偷摸摸的伎俩吗?


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

1
2
3
4
5
6
7
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 aFoo的本征类中创建一个名为a的新方法,可通过常规方法调用语法访问:Foo.a

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

1
2
3
4
5
6
7
8
9
10
11
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的initialize方法中,self不是指Foo类,而是指Foo的某个特定实例。它的本征类是Foo的子类,但它不是Foo;它不可能,否则我们在第二个例子中看到的技巧无法奏效。所以继续你的例子:

1
2
3
4
5
6
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
2
3
4
5
6
7
8
class F
 def eigen
  class << self
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class


Yehuda Katz在解释"Ruby中的元编程:一切都是关于自我"中的细微之处方面做得非常出色