Accesso alle variabili della classe Ruby con class_eval e istanza_eval
-
26-09-2019 - |
Domanda
Ho quanto segue:
class Test
@@a = 10
def show_a()
puts "a: #{@@a}"
end
class << self
@@b = '40'
def show_b
puts "b: #{@@b}"
end
end
end
Perché seguire il lavoro:
Test.instance_eval{show_b}
b: 40
=> nil
Ma non posso accedervi @@b
direttamente?
Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object
Allo stesso modo, il seguente funziona
t = Test.new
t.instance_eval{show_a}
a: 10
=> nil
Ma il seguente fallisce
t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object
Non capisco perché non riesco ad accedere alle variabili di classe direttamente da instance_eval
blocchi.
Soluzione
Ho appena fatto la stessa domanda a Matz durante la festa di Rubykaigi. Ero mezzo ubriaco, ma era perfettamente sobrio, quindi puoi prendere questo come risposta definitiva.
Anton ha ragione: il motivo per cui non è possibile accedere alle variabili di classe tramite Instance_eval () è "solo perché". Anche Class_eval () condivide lo stesso problema (lo stesso Matz non era totalmente sicuro di Class_eval () fino a quando non gli ho detto che l'avrei già provato). Più specificamente: per quanto riguarda l'ambito, le variabili di classe sono più simili a costanti che variabili di istanza, quindi il passaggio di sé (come fanno istanza_eval () e class_eval ()) non farà alcuna differenza quando si tratta di accedervi.
In generale, potrebbe essere una buona idea evitare del tutto le variabili di classe.
Altri suggerimenti
MODIFICARE: Il codice sotto è stato testato con 1.8.7 e 1.9.1 ... sembra che la situazione sia di nuovo diversa con 1.9.2:/
La situazione in realtà non è così semplice. Ci sono differenze nel comportamento a seconda che tu stia usando 1.8 o 1.9 e se stai usando class_eval
o instance_eval
.
Gli esempi di seguito descrivono il comportamento nella maggior parte delle situazioni.
Ho anche incluso il comportamento delle costanti, per una buona misura, poiché il loro comportamento è simile, ma non esattamente lo stesso delle variabili di classe.
Variabili di classe
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
Quindi variabili di classe sono alzato lo sguardo in 1.9 (ma non in 1.8) quando si usa class_eval
instance_eval
In Ruby 1.8 e 1.9
Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable
Sembra che le variabili di classe siano non alzato lo sguardo in 1.8 o 1.9 Quando si utilizza instance_eval
Ciò che è anche interessante è il caso costanti:
Costanti
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
Quindi, come per le variabili di classe, le costanti sono cercate in 1.9 ma non in 1.8 per 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
Sembra che la ricerca costante non sia abbastanza analoga alla variabile di classe Cerca Ruby 1.9. UN Hello
esempio fa ottenere l'accesso alla costante mentre il Hello
la classe no.
Bene, probabilmente la risposta migliore è "solo perché": l'istanza_eval in poche parole crea una sorta di procleton Proc che è invocato con il legame di un determinato oggetto. Sono d'accordo che sia un po 'strano, ma è quello che è.
Se esegui Instance_eval con una stringa, otterrai anche un avvertimento che il tuo metodo cerca di accedere alla variabile di classe:
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
Questo è il modo più conciso e semanticamente corretto che ho trovato per accedere a una variabile di classe:
class Hello
@@foo = :foo_value
end
Hello.class_variable_get :@@foo #=> :foo_value