Variable de instancia: self vs @


179

Aquí hay un código:

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

Lo que quiero saber es la diferencia entre usar @agey self.ageen age_difference_withmétodo.

Respuestas:


260

Escribir @agedirectamente accede a la variable de instancia @age. Escribir self.agele dice al objeto que se envíe el mensaje age, que generalmente devolverá la variable de instancia @age, pero podría hacer cualquier otra cantidad de cosas dependiendo de cómo agese implemente el método en una subclase dada. Por ejemplo, puede tener una clase MiddleAgedSocialite que siempre informa que tiene 10 años menos de lo que realmente es. O más prácticamente, una clase PersistentPerson podría leer perezosamente esos datos de un almacén persistente, almacenar en caché todos sus datos persistentes en un hash.


2
Una vez he leído un libro en rails y no entiendo la diferencia entre este self y @, por lo que siempre debería usar self.var_name en mis métodos (que no establece y obtiene) para hacer que mis datos utilicen una interfaz pública, yo pasó tiempo definiéndolo en getter y setter, ¿verdad?
sarunw el

1
... inglés ... ¿qué quieres decir con cualquier cantidad de cosas? No obtuve los dos últimos ejemplos dados.
user2167582

23

La diferencia es que está aislando el uso del método de la implementación del mismo. Si la implementación de la propiedad cambiara, por ejemplo, mantener la fecha de nacimiento y luego calcular la edad en función de la diferencia de tiempo entre ahora y la fecha de nacimiento, entonces el código dependiendo del método no necesita cambiar. Si usaba la propiedad directamente, entonces el cambio necesitaría propagarse a otras áreas del código. En este sentido, usar la propiedad directamente es más frágil que usar la interfaz proporcionada por la clase.


15
Ohhh, porque self.age podría referirse a una variable de instancia o un método de instancia?
Nolan Amy

@. @ ... triste este es el caso
cyc115

7

Tenga en cuenta cuando herede una clase de la Struct.newcual es una forma ordenada de generar un inicializador ( ¿Cómo generar un inicializador en Ruby? )

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()

volverá

30
nil

Sin embargo, cuando elimine el inicializador, volverá

nil
30

Con la definición de clase

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

Debes proporcionar el constructor.

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

volverá

30
30

Gracias por el ejemplo @Prosseek, actualmente estoy aprendiendo Ruby on Rails y este es exactamente el tipo de comportamiento que me hace sentir que Ruby es innecesariamente complicado>. <.
cyc115

3

La primera respuesta es completamente correcta, pero como un novato relativo no me quedó claro de inmediato lo que implicaba (¿enviarse mensajes a uno mismo? Uh huh ...). Creo que un breve ejemplo ayudará:

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

8
Este ejemplo hizo las cosas más confusas.
Oskar Holmkratz

1
Lo siento, pero el ejemplo no está suficientemente comentado para mí. No puedo seguir tu razonamiento.
kouty

Alguien que vino de Smalltalk dirá que un objeto "se envía un mensaje a sí mismo". Alguien que vino de Python dirá que un objeto "llama a un método en sí mismo". No te confundas; Son exactamente lo mismo. (Un purista semántico puede objetar que solo son iguales para lenguajes con tipeo dinámico y que una llamada a método virtual C ++ no es exactamente lo mismo que enviar un mensaje. El purista es correcto, pero eso probablemente esté fuera del alcance de esta pregunta / respuesta.)
GrandOpener

Me gusta el ejemplo, pero proporcione más comentarios sobre lo que realmente está sucediendo. Difícil de seguir sin explicación
CalamityAdam

2

No hay ninguna diferencia Sospecho que se hizo solo por el valor documental de verse self.agey other_person.agecerca el uno del otro.

Supongo que el uso permite que se escriba un captador real en el futuro, lo que podría hacer algo más complejo que simplemente devolver una variable de instancia, y en ese caso el método no necesitaría cambiar.

Pero es una abstracción poco probable de la que preocuparse, después de todo, si la implementación del objeto cambió, es razonable cambiar otros métodos, en algún momento una referencia simple dentro del objeto en sí es perfectamente razonable.

En cualquier caso, la abstracción de la agepropiedad todavía no explica el uso explícito de self, ya que simplemente agetambién habría invocado el descriptor de acceso.


-3

@age: definitivamente es la edad variable de la instancia

self.age: se refiere a la antigüedad de la propiedad de instancia.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.