2010-03-15 15 views
14

Sono bloccato. Sto provando a definire dinamicamente un metodo di classe e non riesco a spiegarmi il modello di metaclasse rubino. Considerare la seguente classe:Ruby metaclass follia

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 

Ovviamente entrambi i metodi restituiscono un'istanza di Classe. Ma queste due istanze non sono le stesse. Essi hanno anche diversi antenati:

Example.meta.ancestors   # => [Class, Module, Object, Kernel] 
Example.class_instance.ancestors # => [Example, Object, Kernel] 

Qual è il punto a fare la differenza tra la metaclasse e l'istanza di classe?

Ho capito che posso convertire send :define_method nella metaclasse per definire dinamicamente un metodo, ma se provo a inviarlo all'istanza della classe non funzionerà. Almeno potrei risolvere il mio problema, ma voglio ancora capire perché funziona in questo modo.

Aggiornamento Mar 15, 2010 13:40

sono le seguenti ipotesi corretta.

  • Se si dispone di un metodo di istanza che chiama self.instance_eval e definisce un metodo, influirà solo sulla particolare istanza di tale classe.
  • Se si dispone di un metodo di istanza che chiama self.class.instance_eval (che equivale a chiamare class_eval) e definisce un metodo, avrà effetto su tutte le istanze di quella particolare classe risultante in un nuovo metodo di istanza.
  • Se si dispone di un metodo di classe che chiama instance_eval e definisce un metodo, verrà generato un nuovo metodo di istanza per tutte le istanze.
  • Se si dispone di un metodo di classe che chiama instance_eval sulla classe meta/eigen e definisce un metodo, verrà generato un metodo di classe.

Penso che abbia senso per me. Certamente limiterebbe le tue possibilità se self all'interno di un metodo di classe punterebbe alla classe eigen. In tal caso, non sarebbe possibile definire un metodo di istanza all'interno di un metodo di classe. È corretto?

risposta

11

Definizione di un metodo di Singleton in modo dinamico è semplice quando si utilizza instance_eval:

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 

quanto riguarda la differenza di cui sopra, si sta confondendo 2 cose:

La classe meta definito da voi, è quello che ha chiamato in Comunità Ruby come classe singelton o classe eigen. Quella classe singleton è la classe a cui è possibile aggiungere metodi di classe (singleton).

Per quanto riguarda l'istanza di classe che si sta cercando di definire con il metodo class_instance, non è altro che la classe stessa, per dimostrarlo, basta provare ad aggiungere un metodo di istanza alla classe Example e verificare se il metodo class_instance definito da voi rendimenti la classe Example stesso verificando l'esistenza di tale metodo:

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'] 

Comunque per riassumere per voi, quando si desidera aggiungere metodi di classe, basta aggiungere che Metaclasse. Per quanto riguarda il metodo class_instance è inutile, basta rimuoverlo.

Ad ogni modo suggerisco di leggere this post per cogliere alcuni concetti del sistema di riflessione Ruby.

UPDATE

vi consiglio di leggere questo bel post: Fun with Ruby's instance_eval and class_eval, Purtroppo class_eval e instance_eval sono confusa, perché in qualche modo lavorano contro la loro denominazione!

Use ClassName.instance_eval to define class methods. 

Use ClassName.class_eval to define instance methods. 

Ora rispondere alle vostre ipotesi:

Se ho un metodo di istanza che chiamate self.instance_eval e definisce un metodo , interesserà solo il particolare istanza di quella classe.

sì:

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) #=> [] 

Se ho un metodo di istanza che chiamate self.class.instance_eval (che sarebbe lo stesso di chiamare class_eval) e definisce un metodo che sarà influisce su tutte le istanze di quella particolare classe risultante in un nuovo metodo di istanza .

non instance_eval in tale contesto definirà metodi singleton (non quelli istanza) sulla classe stessa:

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"] 

Per funzionare sostituire instance_eval con class_eval sopra.

Se ho un metodo di classe che chiama instance_eval e definisce un metodo che si tradurrà in un nuovo metodo di istanza per tutte le istanze.

No:

class Foo 
    instance_eval do 
    def assumption3() 
     puts 'works' 
    end 
    end 
end 

Foo.instance_methods(false) #=> [] 

Foo.singleton_methods(false) #=> ["assumption_3"] 

che renderà metodi Singleton, non metodi di istanza. Per farlo funzionare sostituire instance_eval con class_eval sopra.

Se ho un metodo di classe che chiama instance_eval sulla classe meta/eigen e definisce un metodo che si tradurrà in un metodo di classe.

beh no, questo renderà le cose così sofisticate, poiché aggiungerà il metodo Singleton alla classe Singleton, non penso che avrà alcun uso pratico.

+0

Per ulteriori informazioni su _why_ a 'def' all'interno di un' instance_eval' definisce i metodi di classe, vedere questo articolo http://yugui.jp/articles/846 – horseyguy

+0

Grazie mille finora. Aggiorno la mia domanda Ti dispiacerebbe dargli un'occhiata? – t6d

+0

Aggiornato per rispondere alle domande nel tuo aggiornamento. – khelll

5

Se si definisce un metodo su un classe, può essere richiamato sulle sue oggetti. È un metodo di istanza .

class Example 
end 

Example.send :define_method, :foo do 
    puts "foo" 
end 

Example.new.foo 
#=> "foo" 

Se si definisce un metodo su un metaclasse, può essere richiamato sul di classe. Questo è simile al concetto di un metodo di classe o statico in altre lingue.

class Example 
    def self.metaclass 
    class << self 
     self 
    end 
    end 
end 

Example.metaclass.send :define_method, :bar do 
    puts "bar" 
end 

Example.bar 
#=> "bar" 

Il ragione che esistano metaclassi è perché si può fare questo in Ruby:

str = "hello" 
class << str 
    def output 
    puts self 
    end 
end 

str.output 
#=> "hello" 

"hi".output 
# NoMethodError 

Come potete vedere, abbiamo definito un metodo che è disponibile solo per uno istanza di un Stringa. La cosa su cui abbiamo definito questo metodo è chiamata metricametria . Nella catena di ricerca del metodo, si accede al metaclass prima di cercare la classe dell'oggetto.

Se sostituiamo l'oggetto di tipo String con un oggetto di tipo Class, che potete immaginare perché questo significa che stiamo definendo solo un metodo su una classe specifica , non in tutte le classi.

Le differenze tra il contesto corrente e self sono sottili, è possibile read more se sei interessato.

Problemi correlati