見た目がとても似ているのに、なぜeigenclassが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設定さselfれるFoo.classためです。

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

回答:


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。このサブクラスは、Foo固有クラスと呼ばれます。だから、def aと呼ばれる新しいメソッドを作成aしてFoo、通常のメソッド呼び出し構文でアクセスの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。そうでなければ、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.

お役に立てれば。


では、各インスタンスは作成されたクラスの匿名のサブクラスですか?
ロバートK

21
各インスタンスのクラスは、作成されたクラスの匿名サブクラスです。f1のクラスはFooの匿名サブクラスであり、FooのクラスはClassの匿名サブクラスです。
David Seiler

6
いい答え:)多くの人はあなたほどはっきりとこれを理解していません。
horseyguy 2010

3
f1の固有クラスは、概念的には、f1の実際のインスタンスとどのように異なりますか。f1がその固有クラスのメソッドにアクセスできる唯一のインスタンスである場合、f1とその固有クラスの種類の違いは壊れていませんか?
elju 2013

1
@eljuええ、ちょっと。本当に重要な違いは、「Foo」と「f1の固有クラス」の違いです。あなたがそれを持っているなら、あなたはおそらく大丈夫です。
David Seiler

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

手すりに同意します。これは素晴らしい答えです
クリストファースコット

3
これは非常に洞察に満ちた有益なコメントです。IFFはすでに上記の@DavidSeilerの回答を読んでいます。
ジャズ

ここでのTheopowerは、発生した例外のデモを行っています。
ニューアレクサンドリア

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.