Отримати значення змінної екземпляра, отримавши її ім’я


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

Відповіді:


179

Найбільш ідіоматичним способом досягнення цього є:

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

Немає необхідності використовувати +або intern; Рубі з цим впорається просто чудово. Однак, якщо ви виявите, що попадаєте на інший об’єкт і витягуєте його івар, є досить велика ймовірність того, що ви порушили інкапсуляцію.

Якщо ви прямо хочете отримати доступ до ivar, правильно зробити, щоб зробити його accessor. Розглянемо наступне:

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}") повертає фактичний об'єкт.
Єгуда Кац

Розкриття мертвої нитки для трішки ясності. Повним прикладом може бути: клас Комп’ютер attr_read: cpus def new (cpus) @cpus = cpus end end?
Ніколас де Фонтене

"Однак, якщо ви виявите, що вдаєтеся до іншого об'єкта і витягуєте його івар, є досить великий шанс, що ви зламали інкапсуляцію.". Як ми потрапляємо в івар іншого об’єкта?
RubyMiner

9

Щоб отримати змінну екземпляра з імені змінної екземпляра, виконайте:

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

Це поверне значення змінної екземпляра @paramName


Під час динамічного створення класу я маю масив імен змінних екземпляра (мені потрібен цей список, оскільки мені потрібно обробляти їх у певному порядку). Використовуючи це ім’я, я хочу отримати посилання на змінну.
LK__

1
У мене є рядок "paramName" і мені потрібно посилання на @paramName
LK__

1
ГАРАЗД. Я рекомендую вам відредагувати своє запитання, щоб сказати саме ці два коментарі.
Даніель Лукрафт 02

2
Крім того, це не має нічого спільного з десеріалізацією. Кращим заголовком може бути "Отримати значення змінної екземпляра із зазначенням її назви" або щось інше.
Даніель Люкрафт

Ви завжди можете зробити модуль і додати його, :attr_reader varnameщоб потім отримати доступ до змінних більш чисто, менш детально.
ocodo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.