Rubin metaclass Wahnsinn
-
20-09-2019 - |
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?
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.