2009-09-15 2 views
19

Gibt es einen Unterschied, wenn Sie Foo mit instance_eval definieren:. . .Ist 'yield self' das selbe wie instance_eval?

class Foo 
    def initialize(&block) 
     instance_eval(&block) if block_given? 
    end 
    end 

. . . oder mit 'Ausbeute Selbst':

class Foo 
    def initialize 
    yield self if block_given? 
    end 
end 

In jedem Fall können Sie dies tun:

x = Foo.new { def foo; 'foo'; end } 
x.foo 

So 'yield self' bedeutet, dass der Block nach Foo.new immer im Zusammenhang mit der ausgewertet wird Foo Klasse.

Ist das korrekt?

Antwort

14

Ihre zwei Code-Teile machen sehr unterschiedliche Dinge. Mit instance_eval werten Sie den Block im Kontext Ihres Objekts aus. Dies bedeutet, dass die Verwendung von def Methoden für dieses Objekt definiert. Es bedeutet auch, dass das Aufrufen einer Methode ohne einen Empfänger innerhalb des Blocks es auf Ihrem Objekt aufrufen wird.

Wenn Sie self übergeben, übergeben Sie self als Argument an den Block, aber da Ihr Block keine Argumente annimmt, wird er einfach ignoriert. Also in diesem Fall macht das Ergebene das selbe wie nichts. Die def verhält sich hier genau wie eine def außerhalb des Blocks würde, ergibt sich nicht tatsächlich ändern, was Sie definieren die Methode auf. Was Sie tun könnten, ist:

Der Unterschied zu instance_eval ist, dass Sie den Empfänger explizit angeben müssen.

bearbeiten zu klären:

In der Version mit Ausbeute obj in dem Block das Objekt sein, das ergibt, der in diesem Fall ist die neu geschaffene Instanz Foo. Während das Selbst den gleichen Wert haben wird, hatte es außerhalb des Blocks. Mit der instance_eval Version self innerhalb des Blocks wird die neu erstellte Foo-Instanz sein.

+0

Meinst du nicht in deinem "Edit to clear", dass das self dem obj im Block zurückgegeben wird? Vielleicht lese ich es nur anders, aber ich sehe das Objekt initialisiert werden, das Selbst wird dem Block als 'obj' übergeben und dann wird die Methode foo innerhalb des Blocks auf self durch obj definiert. – uzo

+1

Ich bin mir ziemlich sicher, wir meinen das Gleiche. Ich schrieb "die neu erstellte Foo-Instanz", weil self innerhalb der initialize-Methode (die neu erstellte Foo-Instanz) nicht das selbe ist wie self innerhalb des Blocks und wenn man nur "self" sagt, ist unklar, welchen man meint. – sepp2k

4

Sie können nur die Stichwort

selbst drop
class Foo 
    def initialize 
    yield if block_given? 
    end 
end 

Update von Kommentaren

Mit Ausbeute es ein bisschen nach meinem Geschmack neu ist, besonders, wenn sie außerhalb irb verwendet.

Allerdings gibt es einen großen und bedeutenden Unterschied zwischen instance_eval Ansatz und Ausbeute Ansatz, lesen Sie in diesem Code-Schnipsel:

class Foo 
    def initialize(&block) 
    instance_eval(&block) if block_given? 
    end 
end 
x = Foo.new { def foo; 'foo'; end }    
#=> #<Foo:0xb800f6a0>            
x.foo #=> "foo"               
z = Foo.new #=> #<Foo:0xb800806c>            
z.foo #=>NoMethodError: undefined method `foo' for #<Foo:0xb800806c> 

prüfen dieses auch:

class Foo2 
    def initialize 
    yield if block_given? 
    end 
end 
x = Foo2.new { def foo; 'foo'; end } #=> #<Foo:0xb7ff1bb4> 
x.foo #=> private method `foo' called for #<Foo2:0xb8004930> (NoMethodError) 
x.send :foo => "foo" 
z = Foo.new #=> #<Foo:0xb800806c> 
z.send :foo => "foo" 

Wie Sie können Der Unterschied besteht darin, dass der erste eine Singleton-Methode foo zu dem Objekt hinzufügt, das initialisiert wird e Das letztere fügt allen Instanzen der Object-Klasse eine private Methode hinzu.

+2

"Wie Sie sehen können, ist der Unterschied, dass der erste eine Singleton-Methode foo zu dem Objekt, das initialisiert wird, hinzufügt, während der letztere diese Methode zu allen Instanzen der Klasse Foo2 hinzufügt" Tatsächlich fügt letzterer die Methode hinzu Objekt (und in echtem Ruby (im Gegensatz zu irb) wird es als private Methode hinzugefügt, so dass Sie x.foo oder z.foo nicht tun können, nur foo). Mit anderen Worten, das def verhält sich genau so, als ob Sie es außerhalb des Blocks geschrieben hätten (es sei denn, die Methode gibt natürlich nicht nach, in welchem ​​Fall nichts passiert). – sepp2k

+0

Sie sind absolut wahr, danke – khelll

+0

In Ruby 1.9 erhalten Sie nicht die private Methodendefinition. Stattdessen x = Foo2.new {def foo; "foo"; Ende} wird die Methode 'foo' auf Objekt definieren (wie sepp2k sagte). Sie können sehen, mit den Worten: x.methods (false) .grep/foo/ # => [] Object.new.foo # => "foo" –

7

Sie sind anders. yield(self) ändert nicht den Wert von self innerhalb des Blocks, während instance_eval(&block) tut.

class Foo 
    def with_yield 
    yield(self) 
    end 

    def with_instance_eval(&block) 
    instance_eval(&block) 
    end 
end 

f = Foo.new 

f.with_yield do |arg| 
    p self 
    # => main 
    p arg 
    # => #<Foo:0x100124b10> 
end 

f.with_instance_eval do |arg| 
    p self 
    # => #<Foo:0x100124b10> 
    p arg 
    # => #<Foo:0x100124b10> 
end 
+0

Der zweite 'p arg' sollte drucken' nil', nicht '# '. – sepp2k

+0

In 1.8.7 wird die Foo-Instanz gedruckt. Ich dachte, es wäre auch nichts, nicht sicher, warum es nicht ist. –

+0

In 1.9 wird nil gedruckt. Ich kann keine Erklärung für das Drucken von 1.8.7 der Foo-Instanz sehen. Sind Sie sicher, dass Sie die Ausgabe nicht falsch gelesen haben? – sepp2k