85

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

为什么对象的特征类与 不同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 元编程的一个偷偷摸摸的把戏?

4

3 回答 3

124

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' eigenclass。所以def a创建了一个aFooeigenclass 中调用的新方法,可以通过正常的方法调用语法访问: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类上定义的,而是在 的 eigenclass 上定义的str,它是 的唯一匿名子类String。所以str有一种frob方法,但String一般情况下没有。我们还可以覆盖 String 的方法(在某些棘手的测试场景中非常有用)。

现在我们有能力理解您的原始示例。Inside的Fooinitialize 方法,self不是指类Foo,而是Foo. 它的 eigenclass 是 的子类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.

希望这可以帮助。

于 2009-10-27T13:59:42.447 回答
49

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

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
于 2009-11-16T16:09:53.633 回答
11

Yehuda Katz 很好地解释了“ Ruby 中的元编程:一切都是为了自我”中的微妙之处

于 2009-12-21T01:38:16.487 回答