Uzyskaj wartość zmiennej instancji, podając jej nazwę

99

Ogólnie, w jaki sposób mogę uzyskać odniesienie do obiektu, którego nazwę mam w ciągu?

Dokładniej, mam listę nazw parametrów (zmienne składowe - zbudowane dynamicznie, więc nie mogę się do nich odwoływać bezpośrednio).

Każdy parametr jest obiektem, który również ma from_smetodę.

Chcę zrobić coś takiego (co oczywiście nie działa ...):

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__
źródło

Odpowiedzi:

179

Najbardziej idiomatycznym sposobem osiągnięcia tego jest:

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

Nie ma potrzeby używania +lub intern; Ruby sobie z tym poradzi. Jeśli jednak zauważysz, że sięgasz do innego obiektu i wyciągasz jego ivar, istnieje dość duża szansa, że ​​złamałeś hermetyzację.

Jeśli wyraźnie chcesz uzyskać dostęp do ivar, właściwą rzeczą jest uczynienie go akcesorium. Rozważ następujące:

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

W takim przypadku, gdybyś to zrobił Computer.new, byłbyś zmuszony użyć, instance_variable_getaby się dostać @cpus. Ale jeśli to robisz, prawdopodobnie @cpuschcesz być publicznie. Co powinieneś zrobić, to:

class Computer
  attr_reader :cpus
end

Teraz możesz to zrobić Computer.new(4).cpus.

Zauważ, że możesz ponownie otworzyć dowolną istniejącą klasę i przekształcić prywatny ivar w czytnik. Ponieważ akcesor to tylko metoda, możesz to zrobićComputer.new(4).send(var_that_evaluates_to_cpus)

Yehuda Katz
źródło
Czy instance_variable_get ("@ # {name}") nie zwraca wartości zmiennej? Potrzebuję odniesienia do rzeczywistego obiektu. Skończyło się na przepisywaniu, więc zamiast mieć wiele zmiennych + tablicę z nazwami w kolejności, w jakiej chciałem, umieściłem parametry w tablicy (decyzja projektowa brzmiała, czy za każdym razem uzyskiwać dostęp do zmiennych, przeszukując tablicę, czy optymalizować za pomocą dodatkowa tablica z ich nazwami, które będą używane tylko w razie potrzeby)
LK__
Nie: instance_variable_get ("@ # {name}") zwraca rzeczywisty obiekt.
Yehuda Katz
Odkrywanie martwej nici dla odrobiny przejrzystości. Kompletny przykład to: class Komputer attr_read: cpus def new (cpus) @cpus = cpus end end?
Nicolas de Fontenay,
„Jeśli jednak odkryjesz, że sięgasz do innego obiektu i wyciągasz jego ivar, istnieje dość duża szansa, że ​​złamałeś hermetyzację.” Jak włamać się do ivar innego obiektu?
RubyMiner
9

Aby pobrać zmienną instancji z nazwy zmiennej instancji, wykonaj:

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

To zwróci wartość zmiennej instancji @paramName

Daniel Lucraft
źródło
Tworząc klasę dynamicznie, mam tablicę nazw zmiennych instancji (potrzebuję tej listy, ponieważ muszę obsługiwać je w określonej kolejności). Używając tej nazwy, chcę uzyskać odniesienie do zmiennej.
LK__
1
Mam ciąg „paramName” i potrzebuję odwołania do @paramName
LK__
1
DOBRZE. Zalecam edycję pytania, aby podać dokładnie te dwa komentarze.
Daniel Lucraft
2
Nie ma to też nic wspólnego z deserializacją. Lepszym tytułem może być „Pobierz wartość zmiennej instancji na podstawie jej nazwy” lub coś w tym stylu.
Daniel Lucraft
Zawsze możesz utworzyć moduł i dodać, :attr_reader varnameaby uzyskać dostęp do zmiennych w bardziej przejrzysty i mniej szczegółowy sposób.
ocodo,