2009-04-02 14 views
12

Lassen Sie uns sagen, dass ich eine Ruby-Klasse haben:Ist es möglich, dass class.property = x etwas anderes als x zurückgibt?

class MyClass 
    def self.property 
    return "someVal" 
    end 

    def self.property=(newVal) 
    # do something to set "property" 
    success = true 

    return success # success is a boolean 
    end 
end 

Wenn ich versuche, und MyClass.property=x tun, ist der Rückgabewert der gesamten Aussage immer x. Es ist eine Konvention in vielen C-basierten/inspirierten Sprachen, einen booleschen "Erfolg" -Wert zurückzugeben - ist es möglich, dies für einen Setter unter Verwendung der "Gleich-Syntax" in Ruby zu tun?

Außerdem - wenn das nicht möglich ist, warum nicht? Gibt es einen denkbaren Nachteil, wenn man einer "Equals Setter" Operation einen Wert zurückgeben kann?

Antwort

11

Ein Nachteil ist, dass Sie die verkettete Zuweisung Semantik brechen würde:

$ irb 
irb(main):001:0> x = y = 3 
=> 3 
irb(main):002:0> p x 
3 
=> nil 
irb(main):003:0> p y 
3 
=> nil 
irb(main):004:0> 

Bedenken Sie:

x = MyClass.property = 3 

Dann xtrue nehmen würde, wenn dies funktioniert, wie Sie es erwartet hatte (rechte Assoziativität). Das könnte eine Überraschung für Leute sein, die Ihre Schnittstelle benutzen und an die typische Semantik gewöhnt sind.

Sie haben auch mich um parallele Zuordnung zu denken, zum Beispiel:

x, y = 1, 2 

Anscheinend ist der Rückgabewert von diesem Ausdruck ist implementation specific ... Ich denke, ich werde nicht parallel Zuweisungen werden Verkettungs :)

Gute Frage!

+0

Ich las dies als „zuerst, wenn Sie einen Accessor so definiert ist, würden Sie die verkettete Zuweisung Semantik brechen ". Wie die Antwort von rampion verdeutlicht, ist dies nicht der Fall: Der Rückgabewert einer Assignment-Methode wird normalerweise ignoriert. Was Martin sagt, ist "die Sprache erlaubt dir das nicht; wenn es so wäre, würdest du die verkettete Zuweisungssemantik durchbrechen". –

5

Ich bin kein Ruby-Experte, aber ich würde für diesen Fall nein sagen, fürchte ich. Ein Property Setter ist nur dazu da, den Wert eines privaten Feldes zu setzen, um keine Nebenwirkungen wie das Zurückgeben von Ergebniscodes zu haben.

Wenn Sie diese Funktionalität wollen dann vergessen Sie den Setter und schreiben Sie eine neue Methode namens TrySetProperty oder etwas, das versucht, die Eigenschaft zu setzen und gibt einen Boolean zurück.

7

Wie Martin sagt, würde dies die Zuordnungsverkettung unterbrechen.

Die Art, wie Ruby-Zuweisungsmethoden definiert sind, um zu arbeiten, expandiert MyClass.property = 3 auf das Äquivalent von (lambda { |v| MyClass.send('property=', v); v })[3] (nicht wirklich, aber das zeigt, wie die Verkettung funktioniert). Der Rückgabewert der Zuweisung ist immer der zugewiesene Wert.

Wenn Sie das Ergebnis Ihrer MyClass#property= Methode sehen möchten, dann #send verwenden:

irb> o = Object.new 
=> #<Object:0x15270> 
irb> def o.x=(y) 
irb> @x = y+1 
irb> puts "y = #{y}, @x = #@x" 
irb> true 
irb> end 
=> nil 
irb> def o.x 
irb> puts "@x = #@x" 
irb> @x 
irb> end 
=> nil 
irb> o.x = 4 
y = 4, @x = 5 
=> 4 
irb> o.x 
@x = 5 
=> 5 
irb> o.send('x=', 3) 
y = 3, @x = 4 
=> true 

jedoch der Rubin Weg, dies zu tun ist, mit Ausnahmen - wenn etwas während die Zuordnung schief geht, heben ein Ausnahme. Dann werden alle Aufrufer muss damit umgehen, wenn etwas schief geht, im Gegensatz zu einem Rückgabewert, die leicht ignoriert werden kann:

# continued from above... 
irb> def o.x=(y) 
irb> unless y.respond_to? :> and (y > 0 rescue false) 
irb>  raise ArgumentError, 'new value must be > 0', caller 
irb> end 
irb> @x = y + 1 
irb> puts "y = #{y}, @x = #@x" 
irb> end 
=> nil 
irb> o.x = 4 
y = 4, @x = 5 
=> 4 
irb> o.x = 0 
ArgumentError: new value must be > 0 
    from (irb):12 
    from :0 
irb> o.x = "3" 
ArgumentError: new value must be > 0 
    from (irb):13 
    from :0 
irb> o.x 
@x = 5 
=> 5