Dapatkan nilai variabel instance dengan namanya

99

Secara umum, bagaimana saya bisa mendapatkan referensi ke objek yang namanya saya miliki dalam sebuah string?

Lebih khusus lagi, saya memiliki daftar nama parameter (variabel anggota - dibangun secara dinamis sehingga saya tidak dapat merujuknya secara langsung).

Setiap parameter adalah objek yang juga memiliki from_smetode.

Saya ingin melakukan sesuatu seperti berikut (yang tentu saja tidak berhasil ...):

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__
sumber

Jawaban:

179

Cara paling idiomatis untuk mencapai ini adalah:

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

Tidak perlu menggunakan +atau intern; Ruby akan menangani ini dengan baik. Namun, jika Anda menemukan diri Anda menjangkau objek lain dan menarik ivar-nya, ada kemungkinan Anda telah merusak enkapsulasi.

Jika Anda secara eksplisit ingin mengakses ivar, hal yang benar untuk dilakukan adalah menjadikannya sebagai pengakses. Pertimbangkan hal berikut:

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

Dalam hal ini, jika Anda melakukannya Computer.new, Anda akan dipaksa untuk menggunakan instance_variable_getuntuk mendapatkan @cpus. Tetapi jika Anda melakukan ini, Anda mungkin bermaksud untuk @cpusmenjadi publik. Yang harus Anda lakukan adalah:

class Computer
  attr_reader :cpus
end

Sekarang Anda bisa melakukannya Computer.new(4).cpus.

Perhatikan bahwa Anda dapat membuka kembali setiap kelas yang ada dan membuat Ivar pribadi ke pembaca. Karena pengakses hanyalah sebuah metode, Anda dapat melakukannyaComputer.new(4).send(var_that_evaluates_to_cpus)

Yehuda Katz
sumber
Bukankah instance_variable_get ("@ # {name}") mengembalikan nilai variabel? Saya perlu referensi ke objek sebenarnya. Saya akhirnya menulis ulang jadi alih-alih memiliki banyak variabel + array dengan nama sesuai urutan yang saya inginkan, saya menempatkan parameter itu sendiri ke dalam array (keputusan desainnya adalah apakah akan mengakses variabel setiap kali dengan mencari array atau mengoptimalkan dengan memiliki array tambahan dengan namanya yang hanya akan digunakan jika diperlukan)
LK__
Tidak: instance_variable_get ("@ # {name}") mengembalikan objek sebenarnya.
Yehuda Katz
Mengungkap benang mati untuk sedikit kejelasan. Contoh lengkapnya adalah: class Computer attr_read: cpus def new (cpus) @cpus = cpus end end?
Nicolas de Fontenay
"Namun, jika Anda menemukan diri Anda menjangkau objek lain dan menarik keluar ivar, ada kemungkinan yang cukup baik bahwa Anda telah merusak enkapsulasi." Bagaimana kita membobol ivar objek lain?.
RubyMiner
9

Untuk mendapatkan variabel instan dari nama variabel instan lakukan:

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

Ini akan mengembalikan nilai variabel contoh @paramName

Daniel Lucraft
sumber
Saat membuat kelas secara dinamis, saya memiliki array nama variabel instan (saya memerlukan daftar ini karena saya perlu menanganinya dalam urutan tertentu). Dengan menggunakan nama ini, saya ingin mendapatkan referensi ke variabel tersebut.
LK__
1
Saya memiliki string "paramName" dan perlu referensi ke @paramName
LK__
1
BAIK. Saya sarankan Anda mengedit pertanyaan Anda untuk mengatakan dua komentar itu dengan tepat.
Daniel Lucraft
2
Selain itu, ini tidak ada hubungannya dengan deserialisasi. Judul yang lebih baik mungkin adalah "Dapatkan nilai variabel instance dengan namanya" atau sesuatu.
Daniel Lucraft
Anda selalu dapat melakukan Module dan menambahkan :attr_reader varnamesehingga Anda dapat mengakses variabel dengan cara yang lebih bersih dan tidak bertele-tele.
ocodo