Obtener el valor de una variable de instancia dado su nombre

99

En general, ¿cómo puedo obtener una referencia a un objeto cuyo nombre tengo en una cadena?

Más específicamente, tengo una lista de los nombres de los parámetros (las variables miembro, creadas dinámicamente, por lo que no puedo referirme a ellas directamente).

Cada parámetro es un objeto que también tiene un from_smétodo.

Quiero hacer algo como lo siguiente (que por supuesto no 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__
fuente

Respuestas:

179

La forma más idiomática de lograr esto es:

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

No es necesario utilizar +o intern; Ruby manejará esto bien. Sin embargo, si se encuentra metiendo la mano en otro objeto y sacando su ivar, existe una probabilidad razonablemente buena de que haya roto la encapsulación.

Si desea acceder explícitamente a un ivar, lo correcto es convertirlo en un descriptor de acceso. Considera lo siguiente:

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

En este caso, si lo hiciera Computer.new, se vería obligado a usar instance_variable_getpara llegar a @cpus. Pero si estás haciendo esto, probablemente quieras hacerlo @cpuspúblico. Lo que debes hacer es:

class Computer
  attr_reader :cpus
end

Ahora puedes hacerlo Computer.new(4).cpus.

Tenga en cuenta que puede volver a abrir cualquier clase existente y convertir un ivar privado en un lector. Dado que un descriptor de acceso es solo un método, puede hacerComputer.new(4).send(var_that_evaluates_to_cpus)

Yehuda Katz
fuente
¿No devuelve instance_variable_get ("@ # {name}") el valor de la variable? Necesito una referencia al objeto real. Terminé reescribiendo, así que en lugar de tener muchas variables + una matriz con los nombres en el orden que quería, puse los parámetros en la matriz (la decisión de diseño fue si acceder a las variables cada vez buscando en la matriz u optimizar al tener un matriz adicional con sus nombres que se usarían solo cuando sea necesario)
LK__
No: instance_variable_get ("@ # {nombre}") devuelve el objeto real.
Yehuda Katz
Descubriendo el hilo muerto para un poco de claridad. El ejemplo completo sería: class Computer attr_read: cpus def new (cpus) @cpus = cpus end end?
Nicolas de Fontenay
"Sin embargo, si se encuentra metiendo la mano en otro objeto y sacando su ivar, hay una probabilidad razonable de que haya roto la encapsulación". ¿Cómo entramos en el ivar de otro objeto?
RubyMiner
9

Para obtener una variable de instancia del nombre de una variable de instancia, haga lo siguiente:

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

Esto devolverá el valor de la variable de instancia. @paramName

Daniel Lucraft
fuente
Al crear una clase dinámicamente, tengo una matriz de los nombres de las variables de instancia (necesito esta lista porque necesito manejarlos en un cierto orden). Al usar este nombre, quiero obtener una referencia a la variable.
LK__
1
Tengo una cadena "paramName" y necesito una referencia a @paramName
LK__
1
OKAY. Te recomiendo que edites tu pregunta para decir exactamente esos dos comentarios.
Daniel Lucraft
2
Además, esto no tiene nada que ver con la deserialización. Un título mejor podría ser "Obtener el valor de una variable de instancia dado su nombre" o algo así.
Daniel Lucraft
Siempre puede hacer un módulo y agregarlo :attr_reader varnamepara poder acceder a las variables de una manera más limpia y menos detallada.
ocodo