Rubys Methode Lookup-Algorithmus ist eigentlich wirklich einfach:
- den
class
Zeiger des
-
Empfänger abgerufen werden, wenn die Methode ist, rufen Sie es
- sonst die
superclass
Zeiger abrufen und wiederholen
Das war's.
Wenn der Algorithmus zu einem Punkt kommt, an dem es keine Superklasse mehr gibt, aber die Methode noch nicht gefunden hat, wird der gesamte Prozess erneut gestartet, wobei method_missing
als Nachricht und der Name der ursprünglichen Nachricht vorangestellt wird zu den Argumenten. Aber das ist es. Das ist der ganze Algorithmus. Es ist sehr klein und sehr einfach, und es hat, um sehr klein und sehr einfach zu sein, weil Methodensuche die am häufigsten durchgeführte Operation in einer objektorientierten Sprache ist.
Hinweis: Ich ignoriere vollständig Module#prepend
/Module#prepend_features
, da ich einfach nicht genug darüber weiß, wie es funktioniert. Ich weiß nur, was es tut, und das ist gut genug für mich.
Beachten Sie auch: Ich ignoriere Leistungsoptimierungen wie das Zwischenspeichern des Ergebnisses einer Methodensuche in etwas wie einem polymorphen Inline-Cache.
Okay, aber hier ist der Trick: Wo genau zeigen die Zeiger class
und superclass
auf? Nun, sie tun nicht zeigen, was die Object#class
und Class#superclass
Methoden zurückgeben. Also, lasst uns ein wenig zurücktreten.
Jedes Objekt verfügt über einen Zeiger class
, der auf die Klasse des Objekts zeigt. Und jede Klasse hat einen Zeiger , der auf seine Oberklasse verweist.
Lassen sich ein laufendes Beispiel starten: Jetzt
class Foo; end
, haben wir Klasse Foo
und seine superclass
Zeiger zeigen auf Object
.
foo = Foo.new
Und unser Objekt foo
‚s class
Zeiger auf Foo
.
def foo.bar; end
Jetzt beginnt die Dinge interessant zu werden. Wir haben eine Singleton-Methode erstellt. Nun, eigentlich gibt es so etwas wie eine Singleton-Methode nicht, es ist wirklich nur eine normale Methode in der Singleton-Klasse. Also, wie funktioniert das? Nun, jetzt zeigt der class
Zeiger auf foo
Singleton Klasse und foo
Singleton Klasse superclass
Zeiger auf Foo
! Mit anderen Worten, die Singleton-Klasse wurde zwischen foo
und ihrer "echten" Klasse Foo
eingefügt.
Wenn wir jedoch foo
über seine Klasse fragen, antwortet er immer noch Foo
:
foo.class #=> Foo
Die Object#class
Methode kennt Singleton-Klassen und einfach überspringt sie, nach dem superclass
Zeiger, bis sie ein „normales finden "Klasse, und gibt das zurück.
nächste Komplikation:
module Bar; end
class Foo
include Bar
end
Was hier passiert? Ruby erstellt eine neue Klasse (nennen wir es Barʹ
), genannt enthalten Klasse. Der Methodentabellenzeiger, der Klassenvariablentabellenzeiger und der Konstanten-Tabellenzeiger dieser Klasse verweisen auf die Methodentabelle, die Klassenvariablentabelle und die Konstantentabelle von Bar
. Dann macht Ruby Barʹ
's superclass
Zeiger auf Foo
' s aktuelle Superklasse und macht Foo
's Superklasse Zeiger auf Barʹ
. Mit anderen Worten, das Einschließen eines Moduls erstellt eine neue Klasse, die als Oberklasse der Klasse eingefügt wird, in der das Modul enthalten ist.
Es gibt eine kleine Komplikation hier: Sie können auch include
Module in Module. Wie funktioniert das? Nun, Ruby verfolgt einfach die Module, die in einem Modul enthalten waren. Und dann, wenn das Modul in einer Klasse enthalten ist, wird es rekursiv die oben genannten Schritte für jedes enthaltene Modul wiederholen.
Und das ist alles, was Sie brauchen, um über die Ruby-Methode Lookup wissen:
- die Klasse finden
- folgen die Oberklasse
- Singletons Klassen oben einfügen Objekte
- umfassen Klassen über den Klassen einfügen
Nun schauen wir uns einige Ihrer Fragen an:
Beim expliziten Aufrufen von Methoden für eine Klasse gibt es viele Abbildungen in der Reihenfolge, in der die von ihnen eingeschlossenen Klassen und Module gesucht werden (und somit genau das, was super
in jedem Fall aufruft). Wenn jedoch nicht explizit eine Methode aufgerufen wird, z.B. eine Ebene func args
anstatt self.func args
Was ist die Suchreihenfolge?
Das Gleiche. self
ist der implizite Empfänger. Wenn Sie keinen Empfänger angeben, lautet der Empfänger self
. Und Klammern sind optional. Mit anderen Worten:
func args
ist genau die gleichen wie
self.func(args)
Warum in meinem Beispiel unten ist, wobei das Element Methode func
findet das Element Verfahren vor dem globalen Aufruf, sondern func2
Funde die globale ohne method_missing
aufgerufen wird?
In Ruby gibt es keine "globale Methode". Es gibt auch keine "Mitgliedsmethode". Jede Methode ist eine Instanzmethode. Zeitraum. Es gibt keine globalen, statischen Klassen-, Singleton-, Member-Methoden, Prozeduren, Funktionen oder Subroutinen.
Eine auf der obersten Ebene definierte Methode wird zu einer private
Instanzmethode der Klasse Object
. Test
erbt von Object
. Führen Sie die Schritte, die ich oben beschrieben, und Sie finden genau, was los ist:
- abrufen
x
‚s Klassenzeiger: Test
- Hat
Test
eine Methode namens func
: Ja, so rufen Sie es.
Jetzt wieder:
- abrufen
x
‚s Klassenzeiger: Test
- hat
Test
ein Verfahren func2
genannt: Nein!
- abrufen
Test
‚s Super Zeiger: Object
- Does
Object
ein Verfahren func2
genannt haben: Ja, so rufen Sie es.
Und wenn die globalen stattdessen ein Modul/Klasse/Typ ist, warum ist das Element mit dem gleichen Namen überhaupt nicht gefunden?
Auch hier gibt es kein global, hier sind keine Mitglieder. Dies hat auch nichts mit Modulen oder Klassen zu tun. Und Ruby hat keine (statischen) Typen.
Math
ist ein Verweis auf eine Konstante. Wenn Sie eine Methode mit demselben Namen aufrufen möchten, müssen Sie sicherstellen, dass Ruby erkennt, dass es sich um eine Methode handelt. Es gibt zwei Dinge, die nur Methoden haben können: einen Empfänger und Argumente. So können Sie entweder fügen Sie einen Empfänger:
self.Math
oder Argumente:
Math()
und jetzt Rubin weiß, dass Sie die Methode Math
und nicht die Konstante Math
bedeuten.
Das gleiche gilt übrigens für lokale Variablen. Und für Setter. Wenn Sie einen Setter aufrufen möchten, statt eine lokale Variable zuzuweisen, müssen Sie sagen:
self.func = 'setter method'
Sie können keine Methoden mit Großbuchstaben definieren, oder? Es wird keinen Fehler bei der Definition auslösen, aber wenn Sie versuchen, es aufzurufen (versuchen Sie 'def Something' anstelle eines bereits definierten Moduls/Klasse) – Ninigi
@Ninigi: Sie können Methoden mit Großbuchstaben definieren, siehe' Array() 'in [ 'Kernel'] (https://ruby-doc.org/core-2.3.1/Kernel.html) – spickermann
@spickermann uh, naja die Quelle ist C Code, aber nicht Ruby ... Es ist nicht' Def Array'. Um etwas klarer zu sein mit dem, was ich meinte: Sie können die Methode DEFINIEREN, aber nicht aufrufen, weil Ruby immer davon ausgeht, dass es eine Konstante ist, keine Methode. – Ninigi