11
class << self
attr_accessor :n, :totalX, :totalY
end

上面的语法用于定义类实例变量。但是当我考虑语法意味着什么时,它对我来说没有任何意义,所以我想知道这种语法是否用于任何其他类型的定义。我在这里的困惑点是:

class << self

附加运算符通常意味着“将右侧的内容添加到左侧的对象”。但是在这个块的上下文中,这怎么加起来就是“把这个块的内容放入类实例而不是实例的定义中”?

出于同样的原因,我很困惑为什么在一个上下文中 class << self 可以定义类实例变量,而在另一个上下文中它似乎创建了类变量,例如:

class Point
  # Instance methods go here
  class << self
    # Class methods go here
  end
end
4

3 回答 3

19

在 Ruby 中,您可以重新打开现有的类并添加方法。也就是说,你可以说:

class Foo
  def bob
    return "hello from bob"
  end
end

这些方法存储在 -class 的内部字典(可能是实例变量)的某个位置Foo(这只是 -class 的一个实例,Class因此具有实例变量)

但令人惊讶的是,您还可以向现有对象的实例添加方法

foo = Foo.new
foo2 = Foo.new

def foo.fred
  return "I am fred"
end


foo.fred  #=> "I am fred"
foo2.fred #=> NoMethodError

但是这种方法实际存储在哪里

原来 Ruby 在幕后创建了一个新类(有时称为单例类元类特征类),它被插入到-class 及其实例之间的继承层次结构中。Foo

所以继承关系是这样的:

foo < (eigenclass of foo) < Foo < Class

(如果你说 foo.superclass 你不会看到单例类)

class << X-syntax 是一种进入这个特殊类的方法,这样你就可以直接操作它。以下代码块完全相同:

def foo.bar
  return "xy"
end

# is exactly the same as

class << foo
  def bar
    return "xy"
  end
end

所以 和 之间的相似性class Foo < Bar并不是class << Foo偶然的,两者都有继承。

认为class << X是“打开 X 的元类”

在 Ruby 中要记住的是,类本身就是对象。(类的实例Class)所以如果你说:

class Foo
  class << self
    def k
      return "x"
    end
  end
end

(在此代码块self中绑定到)也是的 eigenclass 的实例方法,这使其成为 的类方法FookFooFoo

所有这一切都在关于 Pickaxe 类的章节中更清楚地解释(不幸的是,Web 版本不包含图表)和 _whys Seeing Metaclasses Clearly

于 2009-05-22T10:19:05.840 回答
2

将类视为包含成员字典,包括所有访问器和实例变量。当您告诉班级“附加”到“自身”时,您是在说“将这些添加到班级成员的字典中”。

不过,我承认这个符号有点古怪。

于 2009-05-22T05:35:02.927 回答
1

从“附加”运算符的角度来考虑它实际上是令人困惑的。一个更好的看待它的方法是,就像class Foo打开类 Foo 一样,也就是说,它将 'self' 设置为类对象 Foo,并在必要时创建它,因此class << self打开当前 'self' 对象的 eigenclass。请注意,它不限于 self - 对于任何对象 bar,您可以说 class << bar 来打开该对象的 eigenclass。

class A
  def hello
    print "hello world"
  end
end

a = A.new
b = A.new

class << a
  def goodbye
    print "goodbye cruel world"
  end
end

a.hello
b.hello
a.goodbye
b.goodbye
于 2009-05-22T06:33:44.650 回答