Variable d'instance: self vs @


181

Voici un 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

Ce que je veux savoir, c'est la différence entre utiliser @ageet self.agedans la age_difference_withméthode.

Réponses:


265

L'écriture @ageaccède directement à la variable d'instance @age. L'écriture self.ageindique à l'objet de s'envoyer le message age, qui retournera généralement la variable d'instance @age- mais pourrait faire un certain nombre d'autres choses selon la façon dont la ageméthode est implémentée dans une sous-classe donnée. Par exemple, vous pourriez avoir une classe MiddleAgedSocialite qui signale toujours son âge 10 ans plus jeune qu'il ne l'est en réalité. Ou plus pratiquement, une classe PersistentPerson peut lire paresseusement ces données à partir d'un magasin persistant, mettre en cache toutes ses données persistantes dans un hachage.


2
J'ai lu une fois un livre dans les rails et je ne comprends pas la différence entre ce self et @, donc je devrais toujours utiliser self.var_name dans mes méthodes (qui ne définit ni getter) pour rendre mes données en utilisant l'interface publique, je passé du temps à le définir dans getter et setter, non?
sarunw

1
... anglais ... que voulez-vous dire par un certain nombre de choses. je n'ai pas obtenu ces deux derniers exemples.
user2167582

23

La différence est qu'elle isole l'utilisation de la méthode de sa mise en œuvre. Si la mise en œuvre de la propriété devait changer - par exemple pour conserver la date de naissance, puis calculer l'âge en fonction de la différence de temps entre maintenant et la date de naissance - alors le code dépendant de la méthode n'a pas besoin de changer. S'il utilisait directement la propriété, la modification devrait se propager à d'autres zones du code. En ce sens, l'utilisation directe de la propriété est plus fragile que l'utilisation de l'interface fournie par la classe.


15
Ohhh, parce que self.age pourrait faire référence à une variable d'instance ou à une méthode d'instance?
Nolan Amy

@. @ ... triste c'est le cas
cyc115

7

Soyez averti lorsque vous héritez d'une classe à partir de Struct.newlaquelle il existe un moyen efficace de générer un initiateur ( Comment générer un initialiseur dans 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()

reviendra

30
nil

Cependant, lorsque vous supprimez l'initialiseur, il retournera

nil
30

Avec la définition de classe

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

Vous devez fournir le constructeur.

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

reviendra

30
30

Merci pour l'exemple @Prosseek, j'apprends actuellement Ruby on Rails et c'est exactement le genre de comportement qui me fait sentir que Ruby est inutilement compliqué>. <.
cyc115

4

La première réponse est tout à fait correcte, mais en tant que débutant relatif, ce n'était pas immédiatement clair pour moi ce que cela impliquait (envoyer des messages à soi-même? Uh huh ...). Je pense qu'un court exemple aidera:

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
Cet exemple a rendu les choses plus confuses.
Oskar Holmkratz

1
Je suis désolé mais l'exemple n'est pas assez commenté pour moi. Je ne peux pas suivre votre raisonnement.
kouty

Quelqu'un qui vient de Smalltalk dira qu'un objet «s'envoie un message». Quelqu'un qui vient de Python dira qu'un objet «appelle une méthode sur lui-même». Ne soyez pas confus; c'est exactement la même chose. (Un puriste de la sémantique peut objecter qu'ils ne sont identiques que pour les langages à typage dynamique et qu'un appel de méthode virtuelle C ++ n'est pas exactement la même chose que l'envoi d'un message. Le puriste a raison, mais cela dépasse probablement la portée de cette question / réponse.)
GrandOpener

J'aime l'exemple, mais veuillez fournir quelques commentaires supplémentaires sur ce qui se passe réellement. Difficile à suivre sans explication
CalamityAdam

2

Il n'y a aucune différence. Je soupçonne que cela a été fait uniquement pour la valeur documentaire de se voir self.ageet de se other_person.agerapprocher.

Je suppose que l'utilisation permet d'écrire un getter réel dans le futur, ce qui pourrait faire quelque chose de plus complexe que de simplement renvoyer une variable d'instance, et dans ce cas, la méthode n'aurait pas besoin de changer.

Mais c'est une abstraction peu probable dont il faut se soucier, après tout, si l'implémentation de l'objet a changé, il est raisonnable de changer d'autres méthodes, à un moment donné, une simple référence dans l'objet lui-même est parfaitement raisonnable.

Dans tous les cas, l'abstraction de la agepropriété n'explique toujours pas l'utilisation explicite de self, car tout simplement ageaurait également invoqué l'accesseur.


-3

@age - est définitivement l'âge de la variable d'instance

self.age - fait référence à l'âge de la propriété de l'instance.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.