Variável de instância: self vs @


179

Aqui está um 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

O que eu quero saber é a diferença entre usar @agee self.ageno age_difference_withmétodo.

Respostas:


260

A gravação @ageacessa diretamente a variável de instância @age. A gravação self.agediz ao objeto para enviar a mensagem para si mesma age, que geralmente retornará a variável de instância @age- mas poderia fazer várias outras coisas, dependendo de como o agemétodo é implementado em uma determinada subclasse. Por exemplo, você pode ter uma classe MiddleAgedSocialite que sempre relata sua idade 10 anos mais nova do que realmente é. Ou, na prática, uma classe PersistentPerson pode ler preguiçosamente esses dados de um armazenamento persistente, armazenar em cache todos os dados persistentes em um hash.


2
Uma vez li um livro em trilhos e não entendo a diferença entre esse self e @, portanto, sempre devo usar self.var_name nos meus métodos (que não configuram e obtêm) para criar meus dados usando a interface pública. gastou tempo definindo-o em getter e setter, certo?
21923 sarunw

1
... inglês ... o que você quer dizer com inúmeras coisas. não recebi os dois últimos exemplos dados.
user2167582

23

A diferença é que está isolando o uso do método da implementação. Se a implementação da propriedade mudar, digamos, para manter a data de nascimento e, em seguida, calcular a idade com base na diferença de tempo entre agora e a data de nascimento, o código, dependendo do método, não precisa ser alterado. Se ela usasse a propriedade diretamente, a alteração precisaria se propagar para outras áreas do código. Nesse sentido, o uso direto da propriedade é mais frágil do que a interface fornecida pela classe.


15
Ohhh, porque self.age poderia se referir a uma variável de instância ou a um método de instância?
Nolan Amy

. @ @ ... triste este é o caso
cyc115

7

Esteja avisado quando você herdar uma classe da Struct.newqual é uma maneira elegante de gerar um inicializador ( Como gerar o inicializador no 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()

retornará

30
nil

No entanto, quando você remover o inicializador, ele retornará

nil
30

Com a definição de classe

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

Você deve fornecer o construtor.

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

retornará

30
30

Obrigado pelo exemplo @Prosseek, atualmente estou aprendendo Ruby on Rails e esse é exatamente o tipo de comportamento que me faz sentir que Ruby é desnecessariamente complicado>. <.
cyc115

3

A primeira resposta está totalmente correta, mas como um novato relativo, não ficou imediatamente claro para mim o que isso implicava (enviar mensagens para si mesmo? Uh huh ...). Eu acho que um pequeno exemplo ajudará:

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 exemplo tornou as coisas mais confusas.
Oskar Holmkratz 30/07

1
Sinto muito, mas o exemplo não é comentado o suficiente para mim. Eu não posso seguir o seu raciocínio.
Kouty

Alguém que veio do Smalltalk dirá que um objeto "envia uma mensagem para si mesmo". Alguém que veio do Python dirá que um objeto "chama um método por si próprio". Não fique confuso; eles são exatamente a mesma coisa. (Um purista de semântica pode objetar que eles são os mesmos para idiomas com digitação dinâmica e que uma chamada de método virtual C ++ não é exatamente o mesmo que enviar uma mensagem. O purista está correto, mas isso provavelmente está além do escopo desta pergunta / resposta.)
GrandOpener 22/03/19

Gosto do exemplo, mas forneça mais alguns comentários sobre o que realmente está acontecendo. Difícil de seguir sem explicação
CalamityAdam

2

Não há diferença. Suspeito que isso tenha sido feito apenas pelo valor documental de nos vermos self.agee nos other_person.ageaproximarmos.

Suponho que o uso permita que um getter real seja gravado no futuro, o que pode fazer algo mais complexo do que apenas retornar uma variável de instância e, nesse caso, o método não precisaria ser alterado.

Mas é uma abstração improvável se preocupar, afinal, se a implementação do objeto mudou, é razoável alterar outros métodos, em algum momento uma simples referência dentro do próprio objeto é perfeitamente razoável.

De qualquer forma, a abstração da agepropriedade ainda não explica o uso explícito de self, como simplesmente agetambém teria invocado o acessador.


-3

@age - é definitivamente a variável de instância age

self.age - refere-se à idade da propriedade da instância.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.