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設定さselfれるためです。Foo.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ます。したがって、の固有クラスで呼び出される新しいメソッドを作成し、通常のメソッド呼び出し構文でアクセスできます: .def aaFooFoo.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。そうでないと、2 番目の例で見たトリックが機能しません。例を続けるには:

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 は、" Metaprogramming in Ruby: It's All About the Self "で微妙な点をかなりうまく説明しています。

于 2009-12-21T01:38:16.487 に答える