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


83

我在某处错过了备忘录,希望您能向我解释。

为什么对象的本征分类不同于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将设置selfFoo.class用于定义类方法/属性。

我只是感到困惑吗?还是,这是Ruby元编程的一个trick俩?

Answers:


122

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。该子类称为Fooeigenclass。因此,def a创建了一个称为ainFoo的eigenclass的新方法,可以通过常规方法调用语法:进行访问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的initialize方法,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.

希望这可以帮助。


那么,每个实例都是所创建类的匿名子类吗?
罗伯特·K

21
每个实例的都是所创建类的匿名子类。f1的类是Foo的匿名子类,Foo的类是Class的匿名子类。
David Seiler

6
好的答案:)很多人不像您那样清楚地理解这一点。
horseyguy 2010年

3
从概念上讲,f1的本征类与f1的实际实例有何不同?如果f1是唯一可以访问其本征类方法的实例,那么f1和其本征类之间的区别是否会破裂?
elju 2013年

1
@elju是的,有点。真正重要的区别是“ Foo”和“ f1的本征类”之间的区别;如果知道的话,可能还不错。
David Seiler 2013年

46

最简单的答案:本征类不能实例化。

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

您在本网站上可能只有1分,但我喜欢您和您的风格。
horseyguy 2010年

同意栏杆;这是一个很好的答案
Christopher Scott

3
这是一个非常有见地和有益的评论,IFF已经阅读了@DavidSeiler的上述回答。
Jazz

这里的能力就是演示引发的异常。
新亚历山大(Alexandria)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.