Récupère la valeur d'une variable d'instance en fonction de son nom

99

En général, comment puis-je obtenir une référence à un objet dont j'ai le nom dans une chaîne?

Plus précisément, j'ai une liste des noms de paramètres (les variables membres - construites dynamiquement pour que je ne puisse pas y faire référence directement).

Chaque paramètre est un objet qui a également une from_sméthode.

Je veux faire quelque chose comme ce qui suit (qui bien sûr ne fonctionne pas ...):

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__
la source

Réponses:

179

La manière la plus idiomatique d'y parvenir est:

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

Il n'est pas nécessaire d'utiliser +ou intern; Ruby s'en chargera très bien. Cependant, si vous vous trouvez en train d'atteindre un autre objet et de retirer son ivar, il y a de bonnes chances que vous ayez rompu l'encapsulation.

Si vous souhaitez explicitement accéder à un ivar, la bonne chose à faire est d'en faire un accesseur. Considérer ce qui suit:

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

Dans ce cas, si vous le faisiez Computer.new, vous seriez obligé d'utiliser instance_variable_getpour y accéder @cpus. Mais si vous faites cela, vous voulez probablement @cpusêtre public. Ce que vous devez faire est:

class Computer
  attr_reader :cpus
end

Maintenant tu peux le faire Computer.new(4).cpus.

Notez que vous pouvez rouvrir n'importe quelle classe existante et transformer un ivar privé en lecteur. Puisqu'un accesseur n'est qu'une méthode, vous pouvez faireComputer.new(4).send(var_that_evaluates_to_cpus)

Yehuda Katz
la source
Instance_variable_get ("@ # {name}") ne renvoie-t-il pas la valeur de la variable? J'ai besoin d'une référence à l'objet réel. J'ai fini par réécrire donc au lieu d'avoir de nombreuses variables + un tableau avec les noms dans l'ordre que je voulais, j'ai mis les paramètres eux-mêmes dans le tableau (la décision de conception était d'accéder aux variables à chaque fois en recherchant le tableau ou d'optimiser en ayant un tableau supplémentaire avec leurs noms qui ne seraient utilisés qu'en cas de besoin)
LK__
Non: instance_variable_get ("@ # {name}") renvoie l'objet réel.
Yehuda Katz
Découvrir le fil mort pour un peu de clarté. L'exemple complet serait: class Computer attr_read: cpus def new (cpus) @cpus = cpus end end?
Nicolas de Fontenay
"Cependant, si vous vous trouvez en train de tendre la main dans un autre objet et de retirer son ivar, il y a de bonnes chances que vous ayez rompu l'encapsulation.". Comment pénétrer dans l'ivar d'un autre objet?.
RubyMiner
9

Pour obtenir une variable d'instance à partir du nom d'une variable d'instance, procédez comme suit:

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

Cela renverra la valeur de la variable d'instance @paramName

Daniel Lucraft
la source
Lors de la création dynamique d'une classe, j'ai un tableau des noms de variables d'instance (j'ai besoin de cette liste car je dois les gérer dans un certain ordre). En utilisant ce nom, je souhaite obtenir une référence à la variable.
LK__
1
J'ai une chaîne "paramName" et j'ai besoin d'une référence à @paramName
LK__
1
D'ACCORD. Je vous recommande de modifier votre question pour dire exactement ces deux commentaires.
Daniel Lucraft
2
De plus, cela n'a rien à voir avec la désérialisation. Un meilleur titre pourrait être "Obtenir la valeur d'une variable d'instance en fonction de son nom" ou quelque chose.
Daniel Lucraft
Vous pouvez toujours créer un module et l'ajouter :attr_reader varnameafin de pouvoir accéder aux variables d'une manière plus propre et moins verbeuse.
ocodo