14

私は立ち往生しています。クラス メソッドを動的に定義しようとしていますが、Ruby メタクラス モデルについて頭を悩ませることができません。次のクラスを検討してください。

class Example

  def self.meta; (class << self; self; end); end

  def self.class_instance; self; end

end

Example.class_instance.class # => Class
Example.meta.class           # => Class

Example.class_instance  == Example      # => true
Example.class_instance  == Example.meta # => false

明らかに、両方のメソッドが Class のインスタンスを返します。しかし、これら 2 つのインスタンスは同じではありません。また、祖先も異なります。

Example.meta.ancestors            # => [Class, Module, Object, Kernel]
Example.class_instance.ancestors  # => [Example, Object, Kernel]

メタクラスとクラス インスタンスを区別する意味は何ですか?

メソッドを動的に定義するためにメタクラスにできることがわかりましたsend :define_methodが、それをクラスインスタンスに送信しようとすると機能しません。少なくとも私は自分の問題を解決できましたが、なぜこのように機能しているのかを理解したいと思っています。

2010 年 3 月 15 日 13:40 更新

次の仮定は正しいですか。

  • self.instance_eval を呼び出してメソッドを定義するインスタンス メソッドがある場合、そのクラスの特定のインスタンスにのみ影響します。
  • self.class.instance_eval (class_eval の呼び出しと同じ) を呼び出すインスタンス メソッドがあり、メソッドを定義すると、その特定のクラスのすべてのインスタンスに影響し、新しいインスタンス メソッドが生成されます。
  • instance_eval を呼び出してメソッドを定義するクラス メソッドがある場合、すべてのインスタンスに対して新しいインスタンス メソッドが作成されます。
  • meta/eigen クラスで instance_eval を呼び出してメソッドを定義するクラス メソッドがある場合、クラス メソッドになります。

私はそれが私にとって意味を持ち始めていると思います。クラスメソッド内の self が固有クラスを指す場合、可能性は確かに制限されます。その場合、クラス メソッド内からインスタンス メソッドを定義することはできません。あれは正しいですか?

4

2 に答える 2

11

を使用すると、シングルトン メソッドを動的に定義するのは簡単ですinstance_eval

Example.instance_eval{ def square(n); n*n; end }
Example.square(2) #=> 4
# you can pass instance_eval a string as well.
Example.instance_eval "def multiply(x,y); x*y; end" 
Example.multiply(3,9) #=> 27

上記の違いについては、2 つのことを混同しています。

あなたが定義したメタクラスは、Ruby コミュニティでシングルトン クラスまたは固有クラスと呼ばれるものです。そのシングルトン クラスは、クラス (シングルトン) メソッドを追加できるクラスです。

メソッドを使用して定義しようとしているクラス インスタンスについてはclass_instance、クラス自体に他なりません。それを証明するには、クラスにインスタンス メソッドを追加して、定義したメソッドがクラス自体を返すExampleかどうかを確認してください。その方法の:class_instanceExample

class Example
  def self.meta; (class << self; self; end); end
  def self.class_instance; self; end
  def hey; puts hey; end
end

Example.class_instance.instance_methods(false) #=> ['hey']

とにかく要約すると、クラスメソッドを追加したいときは、それらをそのメタクラスに追加するだけです. class_instanceメソッドは役に立たないので、削除してください。

いずれにせよ、Ruby リフレクション システムの概念を理解するために、この記事を読むことをお勧めします。

アップデート

この素敵な投稿を読むことをお勧めします: Ruby の instance_eval と class_eval をお楽しみclass_evalくださいinstance_eval

Use ClassName.instance_eval to define class methods.

Use ClassName.class_eval to define instance methods.

今あなたの仮定に答えます:

self.instance_eval を呼び出してメソッドを定義するインスタンス メソッドがある場合、そのクラスの特定のインスタンスにのみ影響します。

はい:

class Foo
  def assumption1()
    self.instance_eval("def test_assumption_1; puts 'works'; end")
  end
end

f1 = Foo.new
f1.assumption1
f1.methods(false) #=> ["test_assumption_1"]
f2 = Foo.new.methods(false) #=> []

self.class.instance_eval (class_eval の呼び出しと同じ) を呼び出すインスタンス メソッドがあり、メソッドを定義すると、その特定のクラスのすべてのインスタンスに影響し、新しいインスタンス メソッドが生成されます。

そのコンテキストの noinstance_evalは、クラス自体でシングルトン メソッド (インスタンスのものではない) を定義します。

class Foo
  def assumption2()
    self.class.instance_eval("def test_assumption_2; puts 'works'; end")
  end
end

f3 = Foo.new
f3.assumption2
f3.methods(false) #=> []
Foo.singleton_methods(false) #=> ["test_assumption_2"]

それが機能するためには、上記に置き換えinstance_evalてください。class_eval

instance_eval を呼び出してメソッドを定義するクラス メソッドがある場合、すべてのインスタンスに対して新しいインスタンス メソッドが作成されます。

いいえ:

class Foo
  instance_eval do
    def assumption3()
      puts 'works'
    end
  end
end

Foo.instance_methods(false) #=> []

Foo.singleton_methods(false) #=> ["assumption_3"]

これにより、インスタンス メソッドではなく、シングルトン メソッドが作成されます。それが機能するためには、上記に置き換えinstance_evalてください。class_eval

meta/eigen クラスで instance_eval を呼び出してメソッドを定義するクラス メソッドがある場合、クラス メソッドになります。

いいえ、シングルトンクラスにシングルトンメソッドを追加するので、非常に洗練されたものになりますが、実用的ではないと思います。

于 2010-03-15T11:12:04.453 に答える
5

クラスでメソッドを定義すると、そのオブジェクトで呼び出すことができます。インスタンスメソッドです。

class Example
end

Example.send :define_method, :foo do
  puts "foo"
end

Example.new.foo
#=> "foo"

メタクラスでメソッドを定義すると、そのメソッドをクラスで呼び出すことができます。これは、他の言語のクラス メソッドまたは静的メソッドの概念に似ています。

class Example
  def self.metaclass
    class << self
      self
    end
  end
end

Example.metaclass.send :define_method, :bar do
  puts "bar"
end

Example.bar
#=> "bar"

メタクラスが存在する理由は、Ruby でこれを実行できるからです。

str = "hello"
class << str
  def output
    puts self
  end
end

str.output
#=> "hello"

"hi".output
# NoMethodError

ご覧のとおり、String の1 つのインスタンスでのみ使用できるメソッドを定義しました。このメソッドを定義したものは、メタクラスと呼ばれます。メソッド ルックアップ チェーンでは、オブジェクトのクラスを検索する前に、まずメタクラスにアクセスします。

Stringtype のオブジェクトを type のオブジェクトに置き換えると、すべてのクラスではなく、特定Classのクラスでのみメソッドを定義していることになる理由が想像できます。

現在のコンテキスト と の違いselfは微妙です。興味がある場合は、さらに読むことができます。

于 2010-03-15T12:13:38.243 に答える