Instanzvariable: self vs @

179

Hier ist ein Code:

class Person
  def initialize(age)
    @age = age
  end

  def age
    @age
  end

  def age_difference_with(other_person)
    (self.age - other_person.age).abs
  end

  protected :age
end

Was ich wissen möchte, ist der Unterschied zwischen der Verwendung @ageund self.agein age_difference_withVerfahren.

sarunw
quelle

Antworten:

260

Das Schreiben @agegreift direkt auf die Instanzvariable zu @age. Durch das Schreiben self.agewird das Objekt angewiesen, sich selbst die Nachricht zu senden age, die normalerweise die Instanzvariable @agezurückgibt. Je nachdem, wie die ageMethode in einer bestimmten Unterklasse implementiert ist , können jedoch beliebig viele andere Aktionen ausgeführt werden. Beispielsweise haben Sie möglicherweise eine MiddleAgedSocialite-Klasse, deren Alter immer 10 Jahre jünger ist als es tatsächlich ist. In der Praxis kann eine PersistentPerson-Klasse diese Daten aus einem persistenten Speicher träge lesen und alle persistenten Daten in einem Hash zwischenspeichern.

Futter
quelle
2
Ich habe einmal ein Buch in Schienen gelesen und verstehe den Unterschied zwischen diesem Selbst und @ nicht. Daher sollte ich in meinen Methoden (die nicht Setter und Getter sind) immer self.var_name verwenden, um meine Daten über die öffentliche Schnittstelle I zu erstellen verbrachte Zeit damit, es in Getter und Setter zu definieren, richtig?
Sarunw
1
... englisch ... was meinst du mit einer beliebigen Anzahl von Dingen? Die letzten beiden Beispiele habe ich nicht bekommen.
user2167582
23

Der Unterschied besteht darin, dass die Verwendung der Methode von der Implementierung isoliert wird. Wenn sich die Implementierung der Eigenschaft ändern sollte - beispielsweise um das Geburtsdatum beizubehalten und dann das Alter basierend auf der Zeitdifferenz zwischen jetzt und dem Geburtsdatum zu berechnen -, muss sich der Code je nach Methode nicht ändern. Wenn die Eigenschaft direkt verwendet würde, müsste die Änderung auf andere Bereiche des Codes übertragen werden. In diesem Sinne ist die direkte Verwendung der Eigenschaft fragiler als die Verwendung der von der Klasse bereitgestellten Schnittstelle.

Tvanfosson
quelle
15
Ohhh, weil self.age entweder auf eine Instanzvariable oder eine Instanzmethode verweisen könnte?
Nolan Amy
@. @ ... traurig, dass dies der Fall ist
Cyc115
7

Seien Sie gewarnt, wenn Sie eine Klasse erben, von Struct.newder aus Sie einen Intializer erstellen können ( Wie wird ein Initialisierer in Ruby generiert? )

class Node < Struct.new(:value)
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value # or `p value`
    end
end 

n = Node.new(30)
n.show()

wird zurückkehren

30
nil

Wenn Sie jedoch den Initialisierer entfernen, wird er zurückgegeben

nil
30

Mit der Klassendefinition

class Node2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value
    end
end

Sie sollten den Konstruktor angeben.

n2 = Node2.new(30)
n2.show()

wird zurückkehren

30
30
prosseek
quelle
Vielen Dank für das Beispiel @Prosseek, ich lerne gerade Ruby on Rails und genau diese Art von Verhalten gibt mir das Gefühl, dass Ruby unnötig kompliziert ist>. <.
Cyc115
3

Die erste Antwort ist völlig richtig, aber als relativer Neuling war mir nicht sofort klar, was es bedeutete (Nachrichten an sich selbst senden? Huh ...). Ich denke, dass ein kurzes Beispiel helfen wird:

class CrazyAccessors
  def bar=(val)
    @bar = val - 20 # sets @bar to (input - 20)
  end
  def bar
    @bar
  end

  def baz=(value)
    self.bar = value # goes through `bar=` method, so @bar = (50 - 20)
  end

  def quux=(value)
    @bar = value     # sets @bar directly to 50
  end
end

obj  = CrazyAccessors.new
obj.baz = 50
obj.bar  # => 30
obj.quux = 50
obj.bar  # => 50
Klecks
quelle
7
Dieses Beispiel machte die Dinge verwirrender.
Oskar Holmkratz
1
Es tut mir leid, aber das Beispiel ist für mich nicht genug kommentiert. Ich kann Ihrer Argumentation nicht folgen.
Kouty
Jemand, der von Smalltalk kam, wird sagen, dass ein Objekt "eine Nachricht an sich selbst sendet". Jemand, der aus Python stammt, wird sagen, dass ein Objekt "eine Methode für sich selbst aufruft". Sei nicht verwirrt. Sie sind genau das Gleiche. (Ein Semantik-Purist kann einwenden, dass sie nur für Sprachen mit dynamischer Typisierung identisch sind und dass ein virtueller C ++ - Methodenaufruf nicht genau mit dem Senden einer Nachricht identisch ist. Der Purist ist korrekt, aber das geht wahrscheinlich über den Rahmen dieser Frage hinaus. Antwort.)
GrandOpener
Ich mag das Beispiel, aber bitte geben Sie weitere Kommentare dazu ab, was tatsächlich passiert. Schwer zu folgen ohne Erklärung
CalamityAdam
2

Es gibt keinen Unterschied. Ich vermute, dass dies nur für den dokumentarischen Wert des Sehens self.ageund der other_person.ageNähe zueinander getan wurde .

Ich nehme an, dass die Verwendung es ermöglicht, in Zukunft einen tatsächlichen Getter zu schreiben, was etwas Komplexeres bewirken könnte, als nur eine Instanzvariable zurückzugeben, und in diesem Fall müsste sich die Methode nicht ändern.

Aber das ist eine unwahrscheinliche Abstraktion, über die man sich Sorgen machen muss. Wenn sich die Implementierung des Objekts ändert, ist es sinnvoll, andere Methoden zu ändern. Irgendwann ist eine einfache Referenz innerhalb des Objekts selbst durchaus sinnvoll.

In jedem Fall erklärt die Abstraktion der ageEigenschaft immer noch nicht die explizite Verwendung von self, da einfach nur ageder Accessor aufgerufen hätte.

DigitalRoss
quelle
-3

@age - ist definitiv das instanzvariable Alter

self.age - bezieht sich auf das Alter der Instanzeigenschaft.

LEMUEL ADANE
quelle