Получить значение переменной экземпляра по ее имени

99

В общем, как мне получить ссылку на объект, имя которого у меня есть в строке?

В частности, у меня есть список имен параметров (переменные-члены - создаются динамически, поэтому я не могу ссылаться на них напрямую).

Каждый параметр - это объект, у которого также есть from_sметод.

Я хочу сделать что-то вроде следующего (что, конечно, не работает ...):

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__
источник

Ответы:

179

Самый идиоматический способ добиться этого:

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

Нет необходимости использовать +или intern; Ruby прекрасно с этим справится. Однако, если вы обнаруживаете, что тянетесь к другому объекту и вытаскиваете его ivar, есть довольно хороший шанс, что вы нарушили инкапсуляцию.

Если вы явно хотите получить доступ к ivar, правильнее всего сделать его аксессором. Учтите следующее:

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

В этом случае, если бы вы это сделали Computer.new, вам пришлось бы использовать, instance_variable_getчтобы добраться до @cpus. Но если вы делаете это, вы, вероятно, @cpusхотите быть публичным. Что вам следует сделать:

class Computer
  attr_reader :cpus
end

Теперь можно Computer.new(4).cpus.

Обратите внимание, что вы можете повторно открыть любой существующий класс и сделать частный ivar читателем. Поскольку аксессор - это всего лишь метод, вы можете сделатьComputer.new(4).send(var_that_evaluates_to_cpus)

Иегуда Кац
источник
Разве instance_variable_get ("@ # {name}") не возвращает значение переменной? Мне нужна ссылка на реальный объект. В конце концов я переписал так, чтобы вместо множества переменных + массива с именами в желаемом порядке я помещал сами параметры в массив (дизайнерское решение заключалось в том, обращаться ли каждый раз к переменным, выполняя поиск в массиве, или оптимизировать, имея дополнительный массив с их именами, который будет использоваться только при необходимости)
LK__
Нет: instance_variable_get ("@ # {name}") возвращает фактический объект.
Иегуда Кац
Открываем мертвую нить для некоторой ясности. Полный пример: class Computer attr_read: cpus def new (cpus) @cpus = cpus end end?
Николя де Фонтене
«Однако, если вы обнаружите, что тянетесь к другому объекту и вытаскиваете его ivar, есть достаточно хороший шанс, что вы нарушили инкапсуляцию». Как мы взломаем ivar другого объекта?
RubyMiner
9

Чтобы получить переменную экземпляра из имени переменной экземпляра, выполните:

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

Это вернет значение переменной экземпляра @paramName

Дэниел Люкрафт
источник
При динамическом создании класса у меня есть массив имен переменных экземпляра (мне нужен этот список, потому что мне нужно обрабатывать их в определенном порядке). Используя это имя, я хочу получить ссылку на переменную.
LK__ 02
1
У меня есть строка paramName, и мне нужна ссылка на @paramName
LK__
1
ХОРОШО. Я рекомендую вам отредактировать свой вопрос, чтобы сказать именно эти два комментария.
Дэниел Люкрафт 02
2
Кроме того, это не имеет ничего общего с десериализацией. Лучшим заголовком может быть «Получить значение переменной экземпляра по ее имени» или что-то в этом роде.
Дэниел Люкрафт, 02
Вы всегда можете создать модуль и добавить его, :attr_reader varnameчтобы затем получить доступ к переменным более понятным и менее подробным образом.
ocodo