Pergunta

class Hello
@hello = "hello"
    def display
        puts @hello
    end
end

h = Hello.new
h.display

Eu criei a classe acima. Ele não imprime nada de fora. Eu pensei que a variável de instância @hello foi definido durante a declaração de classe. Mas quando eu chamar o método de exibição a saída é 'nulo'. Qual é a maneira correta de fazer isso?

Foi útil?

Solução

As variáveis ??de instância em Ruby pode ser um pouco confuso quando primeiro aprender Ruby, especialmente se você está acostumado a uma outra linguagem OO como Java.

Você não pode simplesmente declarar uma variável de instância.

Uma das coisas mais importantes para saber sobre as variáveis ??de instância em Ruby, além da notação com um prefixo @ sinal, é que que saltar para a vida a primeira vez que são atribuídos a .

class Hello
  def create_some_state
    @hello = "hello"
  end
end

h = Hello.new
p h.instance_variables 

h.create_some_state
p h.instance_variables

# Output
[]
["@hello"]

Você pode usar o Object#instance_variables método para listar todas as variáveis ??de instância de um objeto.

Você normalmente “declarar” e inicializar todas as variáveis ??de instância do método de inicialização. Outra maneira de claramente documento que variáveis ??de instância que deve estar disponível ao público é usar o attr_accessor métodos Módulo (leitura / gravação), attr_writer (escrever) e attr_reader (leitura). Estes métodos irão sintetizar diferentes métodos de acesso para a variável exemplo listado.

class Hello
  attr_accessor :hello
end

h = Hello.new
p h.instance_variables 

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]

A variável de instância ainda não é criado até que seja atribuído a utilizar o método Hello#hello= sintetizado.

Outra questão importante, como kch descrito, é que você precisa estar ciente dos diferentes contextos ativos quando declarar uma classe. Ao declarar uma classe a receptor padrão (auto) no escopo externo será o objeto que representa a classe em si. Daí o seu código vai primeiro criar uma variável de instância de classe ao atribuir a @hello no nível de classe.

métodos dentro de auto será o objeto no qual o método é chamado, portanto, você está tentando imprimir o valor de uma variável de instância com o @hello nome no objeto, que não existe ( nota que é perfeitamente legal ler uma variável de instância não existente).

Outras dicas

Você precisa adicionar um método initialize:

class Hello
    def initialize
        @hello = "hello"
    end
    def display
        puts @hello
    end
end

h = Hello.new
h.display

O primeiro @hello em seu código é chamado de variável de instância de classe.

É uma variável de instância do objeto de classe que os pontos constantes Hello para. (E que é uma instância da classe Class.)

Tecnicamente, quando você está dentro do escopo class, seu self está definido para o objeto de sua classe atual e @variables pertencem ao seu self atual. Boy eu chupar a explicar essas coisas.

Você pode obter tudo isso e muito mais clarificada para você assistindo este conjunto de US $ 5 cada screencasts de The Pragmatic Programmers .

(Ou você pode pedir esclarecimentos aqui e eu vou tentar atualização.)

há uma descrição clara no livro "A linguagem de programação Ruby", lê-lo será muito útil. I colá-lo aqui (a partir do capítulo 7.1.16):

Uma variável de instância usado dentro de uma definição de classe, mas um fora definição método de instância é uma variável de instância class .

class Point
    # Initialize our class instance variables in the class definition itself
    @n = 0              # How many points have been created
    @totalX = 0         # The sum of all X coordinates
    @totalY = 0         # The sum of all Y coordinates

    def initialize(x,y) # Initialize method 
      @x,@y = x, y      # Sets initial values for instance variables
    end

    def self.new(x,y)   # Class method to create new Point objects
      # Use the class instance variables in this class method to collect data
      @n += 1           # Keep track of how many Points have been created
      @totalX += x      # Add these coordinates to the totals
      @totalY += y

      super             # Invoke the real definition of new to create a Point
                    # More about super later in the chapter
    end

    # A class method to report the data we collected
    def self.report
        # Here we use the class instance variables in a class method
        puts "Number of points created: #@n"
        puts "Average X coordinate: #{@totalX.to_f/@n}"
        puts "Average Y coordinate: #{@totalY.to_f/@n}"
    end
end

......

Como as variáveis ??de instância de classe são apenas variáveis ??de instância de classe objetos, podemos usar attr, attr_reader e attr_accessor para criar métodos de acesso para eles.

class << self
  attr_accessor :n, :totalX, :totalY
end

Com estes acessores definido, podemos nos referir aos nossos dados brutos como Point.n, Point.totalX e Point.totalY.

Eu tinha esquecido que havia um conceito de "classe variável de instância" em Ruby. Em qualquer caso, o problema do OP parecia intrigante, e não foi realmente abordada em nenhuma das respostas até então, com exceção de uma dica na resposta de kch: é um problema do escopo. (Adicionado em edit: Na verdade, a resposta de IRSs faz endereço este ponto no final, mas eu vou deixar esta resposta se de qualquer maneira, como eu acho que o código de exemplo pode ser útil para a compreensão do problema.)

Em uma classe Ruby, um nome de variável começando com @ pode se referir a um dos dois variáveis: ou um variável de instância ou instância de classe variável , dependendo de onde na classe é referido. Esta é uma pegadinha bastante sutil.

Um exemplo irá esclarecer o ponto. Aqui está uma pequena classe de teste Ruby (todo o código testado no IRB):

class T

  @@class_variable = "BBQ"
  @class_instance_variable_1 = "WTF"
  @class_instance_variable_2 = "LOL"

  def self.class_method
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

  def initialize
    @instance_variable = "omg"
    # The following line does not assign a value to the class instance variable,
    # but actually declares an instance variable withthe same name!
    @class_instance_variable_1 = "wtf"
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    # The following two lines do not refer to the class instance variables,
    # but to the instance variables with the same names.
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

  def instance_method
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    # The following two lines do not refer to the class instance variables,
    # but to the instance variables with the same names.
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

end

I chamado as variáveis ??de acordo com o que eu pensei que eles eram, embora isso acaba por não para sempre ser o caso:

irb> T.class_method
@@class_variable           == BBQ
@class_instance_variable_1 == WTF    # the value of the class instance variable
@class_instance_variable_2 == LOL    # the value of the class instance variable
@instance_variable         == nil    # does not exist in the class scope
=> nil

irb> t = T.new
@@class_variable           == BBQ
@class_instance_variable_1 == wtf    # the value of the instance variable
@class_instance_variable_2 == nil    # the value of the instance variable
@instance_variable         == omg
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf">

irb> t.instance_method
@@class_variable           == BBQ
@class_instance_variable_1 == wtf    # the value of the instance variable
@class_instance_variable_2 == nil    # the value of the instance variable
@instance_variable         == omg
=> nil

irb> T.class_method
@@class_variable           == BBQ
@class_instance_variable_1 == WTF    # the value of the class instance variable
@class_instance_variable_2 == LOL    # the value of the class instance variable
@instance_variable         == nil    # does not exist in the class scope
=> nil

O @@class_variable e @instance_variable sempre se comportam como você esperaria: o primeiro é definido no nível de classe, e se referida em um método de classe ou em um método de instância, que detém valor atribuído a ele no topo. Este último só recebe um valor em um objeto de T classe, portanto, em um método de classe, ele se refere a uma variável desconhecida cujo valor é nil.

O método de classe imaginativamente chamado saídas class_method os valores de @@class_variable e os dois @class_instance_variables como esperado, isto é, como inicializado no topo da classe. No entanto, na initialize métodos de instância e instance_method, diferentes variáveis ?? com o mesmo nome são acessados, ou seja, as variáveis ?? exemplo, não de classe variáveis ??de instância .

Você pode ver que a atribuição no método initialize não afetou a @class_instance_variable_1 variável de instância de classe, porque a chamada mais tarde de class_method gera o seu valor antigo, "WTF". Em vez disso, o método initialize declarou uma nova variável de instância, um que é também chamado (erroneamente) @class_instance_variable_1. O valor atribuído a ele, "wtf", é emitido por métodos initialize e instance_method.

O @class_instance_variable_2 variável no código de exemplo é equivalente a @hello variável no problema original: é declarada e inicializada como uma variável de instância de classe, mas quando um método de instância refere-se a uma variável com esse nome, ele realmente vê um variável de instância com o mesmo nome -. uma que nunca foi declarado, por isso seu valor é nulo

Eu também recomendo olhando para variáveis ??de classe que são prefixados com "@@" - aqui está um código de exemplo para mostrar como classe e instância vars são diferentes:

class Vars
  @@classvar="foo"
  def test
    @instancevar="bar"
  end
  def Vars.show
    puts "classvar: #{@@classvar}"
    puts "instancevar: #{@instancevar}"
  end
  def instance_show
    puts "classvar: #{@@classvar}"
    puts "instancevar: #{@instancevar}"

  end
end

# only shows classvar since we don't have an instance created
Vars::show
# create a class instance
vars = Vars.new
# instancevar still doesn't show b/c it hasn't been initialized
vars.instance_show
# initialize instancevar
vars.test
# now instancevar shows up as we expect
vars.instance_show
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top