Zugriff auf Ruby -Klasse -Variablen mit class_eval und instance_eval
-
26-09-2019 - |
Frage
Ich habe Folgendes:
class Test
@@a = 10
def show_a()
puts "a: #{@@a}"
end
class << self
@@b = '40'
def show_b
puts "b: #{@@b}"
end
end
end
Warum funktioniert die folgende Arbeit:
Test.instance_eval{show_b}
b: 40
=> nil
Aber ich kann nicht zugreifen @@b
direkt?
Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object
Ebenso funktioniert die folgenden Arbeiten
t = Test.new
t.instance_eval{show_a}
a: 10
=> nil
Aber das folgende schlägt fehl
t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object
Ich verstehe nicht, warum ich nicht auf die Klassenvariablen direkt von der zugreifen kann instance_eval
Blöcke.
Lösung
Ich habe Matz gerade die gleiche Frage während der Rubykaigi -Party gestellt. Ich war halb betrogen, aber er war vollkommen nüchtern, also können Sie dies als endgültige Antwort betrachten.
Anton hat Recht - der Grund, warum Sie nicht über Instance_eval () auf Klassenvariablen zugreifen können, ist "nur weil". Sogar Class_eval () teilt das gleiche Problem (Matz selbst war sich nicht ganz sicher über class_eval (), bis ich ihm sagte, ich hätte es bereits versucht). Genauer gesagt: Klassenvariablen sind eher Konstanten als Instanzvariablen, sodass das Umschalten des Selbst (als Instance_eval () und class_eval () do) keinen Unterschied macht, wenn es um den Zugriff auf sie zugänglich ist.
Im Allgemeinen könnte es eine gute Idee sein, Klassenvariablen insgesamt zu vermeiden.
Andere Tipps
BEARBEITEN: Der folgende Code wurde mit 1.8.7 und 1.9.1 getestet ... Es scheint, dass die Situation wieder mit 1.9.2:/ anders ist.
Die Situation ist eigentlich nicht so einfach. Es gibt Unterschiede im Verhalten, je nachdem, ob Sie 1.8 oder 1,9 verwenden und ob Sie verwenden class_eval
oder instance_eval
.
In den folgenden Beispielen wird das Verhalten in den meisten Situationen beschrieben.
Ich habe auch das Verhalten von Konstanten für ein gutes Maß eingeschlossen, da ihr Verhalten ähnlich ist, aber nicht genau das gleiche wie Klassenvariablen.
Klassenvariablen
class_eval
in Ruby 1.8:
class Hello
@@foo = :foo
end
Hello.class_eval { @@foo } #=> uninitialized class variable
class_eval
in Ruby 1.9:
Hello.class_eval { @@foo } #=> :foo
Also Klassenvariablen sind schaute in 1,9 (aber nicht in 1,8) bei der Verwendung auf class_eval
instance_eval
in Ruby 1.8 und 1.9
Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable
Es scheint Klassenvariablen zu sein, sind nicht schaute in 1,8 auf oder 1.9 Bei Verwendung instance_eval
Was auch interessant ist, ist für den Fall Konstanten:
Konstanten
class_eval
in Ruby 1.8
class Hello
Foo = :foo
end
Hello.class_eval { Foo } #=> uninitialized constant
class_eval
in Ruby 1.9
Hello.class_eval { Foo } #=> :foo
Also, wie bei Klassenvariablen, werden Konstanten in 1,9, aber aber nicht in 1,8 für class_eval
instance_eval
in Ruby 1.8
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant
instance_eval
in Ruby 1.9
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo
Es scheint, dass konstante Suche nicht ganz analog zur Klassenvariablen -Such -Such nach Ruby 1.9 ist. EIN Hello
Beispiel tut Erhalten Sie Zugang zur Konstante, während die Hello
Klasse nicht.
Die beste Antwort lautet wahrscheinlich "Nur weil": Die Instanz -Eval erstellt eine Art Singleton -Proc, das mit der Bindung eines bestimmten Objekts aufgerufen wird. Ich stimme zu, dass das ist ein bisschen seltsam, aber es ist was es ist.
Wenn Sie instance_eval mit einer Zeichenfolge ausführen, erhalten Sie sogar eine Warnung, dass Ihre Methode versucht, auf Klassenvariable zuzugreifen:
irb(main):038:0> Test.new.instance_eval "@@a"
(eval):1: warning: class variable access from toplevel singleton method
NameError: (eval):1:in `irb_binding': uninitialized class variable ...
Ruby 2.1
Dies ist die prägnanteste und semantisch korrektste Art und Weise, wie ich auf eine Klassenvariable zugegriffen habe:
class Hello
@@foo = :foo_value
end
Hello.class_variable_get :@@foo #=> :foo_value