Frage

Ich bin fest. Ich versuche, dynamisch eine Klassenmethode zu definieren, und ich kann meinen Kopf nicht umschlingen den Rubin metaclass Modell. Betrachten Sie die folgende Klasse:

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

Offensichtlich beide Methoden eine Instanz der Klasse zurück. Aber diese beiden Instanzen sind nicht das Gleiche. Sie haben auch verschiedene Vorfahren:

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

Was ist der Punkt in einen Unterschied zwischen den Metaklasse und der Klasseninstanz?

Ich fand heraus, dass ich auf die Metaklasse send :define_method kann dynamisch eine Methode zu definieren, aber wenn ich versuche, es in die Klasseninstanz senden wird es nicht funktionieren. Wenigstens konnte ich mein Problem lösen, aber ich will noch verstehen, warum es so funktioniert.

Update 15. März 2010 13.40 Uhr

Sind die folgenden Annahmen korrekt.

  • Wenn ich eine Instanz Methode haben, die self.instance_eval nennt und definiert eine Methode, es wird nur die besondere Instanz dieser Klasse beeinflussen.
  • Wenn ich eine Instanz Methode haben, die self.class.instance_eval nennt (die die gleiche wie der Aufruf von class_eval wäre) und definiert eine Methode, sie alle Instanzen dieser bestimmten Klasse auswirken wird in einer neuen Instanz Methode zur Folge hat.
  • Wenn ich eine Klassenmethode, die instance_eval nennt und definiert eine Methode, um es in einer neuen Instanz Methode für alle Instanzen führen.
  • Wenn ich eine Klassenmethode, die auf der meta / EIGEN Klasse instance_eval nennt und definiert eine Methode in einer Klassenmethode führen.

Ich denke, es beginnt Sinn für mich zu machen. Es wäre sicherlich Ihre Möglichkeiten einschränken, wenn sich innerhalb einer Klassenmethode auf die Eigen Klasse zeigen würde. Wenn dem so ist es nicht möglich wäre, eine Instanz-Methode aus dem Inneren einer Klassenmethode zu definieren. Ist das richtig?

War es hilfreich?

Lösung

dynamisch eine Singleton-Methode zu definieren ist einfach, wenn man instance_eval verwenden:

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

Wie für die Differenz oben, Sie sind verwirrend 2 Dinge:

Die Meta-Klasse von Ihnen festgelegt, was in Ruby-Community als Singelton Klasse oder EIGEN Klasse genannt. Die Singleton-Klasse ist die Klasse, dass Sie Klasse hinzufügen können (Singleton) Methoden.

Wie für die Klasseninstanz Sie versuchen, die class_instance Methode zu definieren, verwendet wird, ist nichts anderes als die Klasse selbst, um es zu beweisen, versuchen Sie einfach eine Instanz Methode zur Klasse Example hinzufügen und überprüfen, ob die class_instance Methode von Ihnen definierten kehrt die Klasse Example sich durch die Existenz dieses Verfahren überprüft:

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']

Wie auch immer es für Sie zusammenzufassen, wenn Sie Klassenmethoden hinzufügen möchten, fügen Sie sie einfach auf diese Meta-Klasse. Wie für die class_instance Methode nutzlos ist, nur um es zu entfernen.

Auf jeden Fall empfehle ich Ihnen, lesen Sie diesen Beitrag einige Konzepte von Ruby zu erfassen Reflexionssystem.

UPDATE

Ich schlage vor, Sie diesen schönen Beitrag lesen: Spaß mit Rubys instance_eval und class_eval , Leider class_eval und instance_eval sind verwirrend, weil sie irgendwie Arbeit gegen ihre Namensgebung!

Use ClassName.instance_eval to define class methods.

Use ClassName.class_eval to define instance methods.

Jetzt ist Ihre Annahmen zu beantworten:

  

Wenn ich eine Instanz Methode haben die   self.instance_eval nennt und definiert eine   Verfahren, wird es nur Auswirkungen auf die   bestimmte Instanz dieser Klasse.

ja:

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) #=> []
  

Wenn ich eine Instanz Methode haben die   ruft self.class.instance_eval (die   wäre das gleiche wie der Aufruf   class_eval) und definiert eine Methode, um es   werden alle Instanzen, die beeinflussen,   besondere Klasse, was zu einem neuen   Instanzmethode.

keine instance_eval in diesem Zusammenhang werden Singleton-Methoden definieren (nicht Instanz sind) auf die Klasse selbst:

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"]

Um das zu erreichen Arbeit ersetzen instance_eval mit class_eval oben.

  

Wenn ich eine Klassenmethode, die Anrufe   instance_eval und definiert eine Methode, um es   in einer neuen Instanz Methode führt   für alle Instanzen.

Nein:

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

Foo.instance_methods(false) #=> []

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

wird Die Singleton-Methoden machen, nicht Instanzmethoden. Um das zu erreichen Arbeit ersetzen instance_eval mit class_eval oben.

  

Wenn ich eine Klassenmethode, die Anrufe   instance_eval auf der Meta / EIGEN Klasse   und definiert ein Verfahren, wird es in Folge   eine Klassenmethode.

auch nicht, dass so anspruchsvolle Sachen machen, wie es Singleton-Methode auf die Singleton-Klasse hinzufügen wird, ich glaube nicht, das wird keine praktische Verwendung hat.

Andere Tipps

Wenn Sie eine Methode auf einem Klasse definieren , kann es auf seine aufgerufen werden Objekte . Es ist eine Instanzmethode .

class Example
end

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

Example.new.foo
#=> "foo"

Wenn Sie definieren eine Methode auf einem metaclass , kann es auf der Seite Klasse aufgerufen werden . Dies ist vergleichbar mit dem Konzept einer Klassenmethode oder statischer Methode in anderen Sprachen.

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

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

Example.bar
#=> "bar"

Der Grund , dass metaclasses vorhanden ist, weil Sie dies tun, in Ruby können:

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

str.output
#=> "hello"

"hi".output
# NoMethodError

Wie Sie sehen können, definiert man eine Methode, die nur für ist ein Instanz eines String. Die Sache, dass wir diese Methode definiert wird, um die auf metaclass genannt. Bei dem Verfahren Lookup-Kette wird die Metaklasse zuerst zugegriffen, bevor die Klasse des Objekts zu suchen.

Wenn wir das Objekt vom Typ String mit einem Objekt vom Typ Class ersetzen, können Sie sich vorstellen, warum diese Mittel wir nur eine Methode für ein spezifische Klasse definiert, nicht auf alle Klassen.

Die Unterschiede zwischen dem aktuellen Kontext und self subtil sind, können Sie lesen mehr wenn Sie interessiert sind.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top