Pega o valor de uma variável de instância dado seu nome

99

Em geral, como posso obter uma referência a um objeto cujo nome tenho em uma string?

Mais especificamente, tenho uma lista dos nomes dos parâmetros (as variáveis ​​de membro - construídas dinamicamente para que não possa me referir a elas diretamente).

Cada parâmetro é um objeto que também possui um from_s método.

Eu quero fazer algo como o seguinte (que é claro não funciona ...):

define_method(:from_s) do | arg |
    @ordered_parameter_names.each do | param |
        instance_eval "field_ref = @#{param}"
        field_ref.from_s(param)
    end
end
LK__
fonte

Respostas:

179

A maneira mais idiomática de conseguir isso é:

some_object.instance_variable_get("@#{name}")

Não há necessidade de usar +ou intern; Ruby vai lidar com isso muito bem. No entanto, se você se pegar alcançando outro objeto e retirando seu ivar, há uma chance razoavelmente boa de que você quebrou o encapsulamento.

Se você deseja acessar explicitamente um ivar, a coisa certa a fazer é torná-lo um acessador. Considere o seguinte:

class Computer
  def new(cpus)
    @cpus = cpus
  end
end

Neste caso, se o fizesse Computer.new, seria forçado a usar instance_variable_getpara chegar @cpus. Mas se você está fazendo isso, provavelmente pretende @cpusser público. O que você deve fazer é:

class Computer
  attr_reader :cpus
end

Agora você pode fazer Computer.new(4).cpus.

Observe que você pode reabrir qualquer classe existente e transformar um ivar privado em um leitor. Uma vez que um acessador é apenas um método, você pode fazerComputer.new(4).send(var_that_evaluates_to_cpus)

Yehuda Katz
fonte
Instance_variable_get ("@ # {name}") não retorna o valor da variável? Preciso de uma referência ao objeto real. Acabei reescrevendo, então, em vez de ter muitas variáveis ​​+ um array com os nomes na ordem que eu queria, coloquei os próprios parâmetros no array (a decisão do projeto era acessar variáveis ​​a cada vez pesquisando o array ou otimizar tendo um matriz adicional com seus nomes que seriam usados ​​apenas quando necessário)
LK__
Não: instance_variable_get ("@ # {name}") retorna o objeto real.
Yehuda Katz
Descobrindo o fio morto para um pouco de clareza. O exemplo completo seria: class Computer attr_read: cpus def new (cpus) @cpus = cpus end end?
Nicolas de Fontenay
"No entanto, se você se pegar alcançando outro objeto e retirando seu ivar, há uma chance razoavelmente boa de que você tenha quebrado o encapsulamento." Como invadimos o ivar de outro objeto?.
RubyMiner
9

Para obter uma variável de instância a partir do nome de uma variável de instância, faça:

name = "paramName"
instance_variable_get(("@" + name).intern)

Isso retornará o valor da variável de instância @paramName

Daniel Lucraft
fonte
Ao criar uma classe dinamicamente, tenho uma matriz de nomes de variáveis ​​de instância (preciso dessa lista porque preciso tratá-los em uma determinada ordem). Ao usar esse nome, desejo obter uma referência para a variável.
LK__
1
Eu tenho uma string "paramName" e preciso de uma referência a @paramName
LK__
1
ESTÁ BEM. Recomendo que você edite sua pergunta para dizer exatamente esses dois comentários.
Daniel Lucraft
2
Além disso, isso não tem nada a ver com a desserialização. Um título melhor pode ser "Pegue o valor de uma variável de instância dado seu nome" ou algo assim.
Daniel Lucraft
Você sempre pode fazer um Módulo e adicionar :attr_reader varnamepara que possa acessar as variáveis ​​de uma maneira mais limpa e menos detalhada.
ocodo