web-dev-qa-db-ja.com

eigenclassが非常に似ているのに、なぜself.classと同等ではないのですか?

どこかでメモを見逃したので、これについて説明してもらえれば幸いです。

オブジェクトの固有クラスが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へのショートカットです。

したがって、クラスオブジェクトへの参照内では、selfself.classと同じである必要があります。これは、クラスメソッド/属性の定義のためにclass << selfselfFoo.classに設定するためです。

私は混乱していますか?または、これはRubyメタプログラミングの卑劣なトリックですか?

82
Robert K

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は、aの固有クラスにFooという新しいメソッドを作成します。これは、通常のメソッド呼び出し構文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の初期化メソッド内では、selfはクラスFooではなく、Fooの特定のinstanceを参照します。その固有クラスは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.

お役に立てれば。

121
David Seiler

最も簡単な答え:固有クラスはインスタンス化できません。

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

イェフダ・カッツは、「 Rubyでのメタプログラミング:自己についてのすべて 」で微妙な点について説明しています。

11
Pete Hodgson