2016-03-08 5 views
6

(Domanda già pubblicata allo Ruby Forum, ma non ha evocato alcuna risposta lì).Il metodo di istanza aggiunto dinamicamente non può accedere alla variabile di classe

Questo è il mio codice:

class MC 
    def initialize 
    @x = 5 
    @@y = 6 
    end 

    def f 
    puts @x 
    puts @@y 
    end 
end 

m = MC.new 
m.f 

m.f produce i risultati attesi, senza un errore:

5 
6 

Ma questo:

def m.g 
    puts @x 
    puts @@y 
end 

m.g 

produce:

5 
warning: class variable access from toplevel 
NameError: uninitialized class variable @@y in Object 

Perché è possibile accedere a @@y da f, ma non da g?

La menzione di toplevel e Object nell'avviso e il messaggio di errore mi lascia perplessi.

@x viene stampato come 5, quindi l'ambiente è MC. Ciò esclude la possibilità che @x e @@y nella definizione di m.g faccia riferimento all'ambiente di primo livello (Object) anziché MC.

Perché ho ricevuto il messaggio di errore?

+3

Questo è stato chiesto prima: http://stackoverflow.com/q/24510948/477037 – Stefan

+1

@Stefan: vero. Sfortunatamente, non c'è una buona spiegazione anche lì. –

risposta

4

Non si crea g nella classe MC ma nella classe Singleton m (a.k.a. eigenclass).

Questa è una classe esistente appositamente per l'oggetto m per memorizzare i metodi singleton definiti solo per m.

+0

'classe << m; mette class_variable_get (: @@ y); fine' funziona perfettamente. Così come 'def m.g; mette self.class.class_variable_get (: @@ y); FINE'. Il che è un po 'strano – mudasobwa

+0

Si noti che questa è solo una parte della risposta. La classe singleton è una sottoclasse di 'MC' (' m.singleton_class.superclass # => MC') e quindi "vede" '@@ y' (' m.singleton_class.class_variables # => [: @yy] '). Mi manca perché la ricerca fallisce. –

+0

Immagino sia un errore del parser. – mudasobwa

5

Tutte le varianti di sotto di lavoro:

def m.g; puts self.class.send(:class_eval, '@@y') end 

def m.g; puts self.class.class_variable_get(:@@y) end 

class << m; def g; puts self.class.send(:class_eval, '@@y') end end 

class << m; puts class_variable_get(:@@y) end 

Ma questi non riescono:

def m.g; puts @@y; end 

class << m; puts class_eval('@@y') end 

vorrei prendere in considerazione questo essere un problema tecnico rubino parser.

+0

Si noti che il metodo '# class'" salta "la classe singleton e restituisce' MC'. Quindi i primi 3 non sono davvero sorprendenti. Dovresti anche aggiungere le varianti con 'singleton_class' che mostrano lo stesso comportamento. –

+1

Sembra che le regole per la ricerca costante si applichino anche alle variabili di classe, cioè sono con scope lessicale. – Stefan

+0

@undur_gongor 'class << m' sono le varianti con' singleton_class'. – mudasobwa