الوصول إلى متغيرات فئة Ruby مع class_eval و eastival_eval
-
26-09-2019 - |
سؤال
لدي ما يلي:
class Test
@@a = 10
def show_a()
puts "a: #{@@a}"
end
class << self
@@b = '40'
def show_b
puts "b: #{@@b}"
end
end
end
لماذا يتبع العمل:
Test.instance_eval{show_b}
b: 40
=> nil
لكن لا يمكنني الوصول @@b
مباشرة؟
Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object
وبالمثل ، الأعمال التالية
t = Test.new
t.instance_eval{show_a}
a: 10
=> nil
لكن ما يلي يفشل
t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object
لا أفهم لماذا لا يمكنني الوصول إلى متغيرات الفصل مباشرة من instance_eval
كتل.
المحلول
لقد سألت للتو نفس السؤال إلى ماتز خلال حفلة Rubykaigi. كنت نصف قطر ، لكنه كان رصينًا تمامًا ، حتى تتمكن من أخذ هذا الإجابة النهائية.
أنطون على حق - السبب في عدم تمكنك من الوصول إلى متغيرات الفئة من خلال evonleal_eval () هو "لمجرد". حتى class_eval () يشارك نفس المشكلة (لم يكن Matz نفسه متأكدًا تمامًا من class_eval () حتى أخبرته أنني جربتها بالفعل). بشكل أكثر تحديداً: إن متغيرات الفئة من النطاق ، تشبه الثوابت أكثر من متغيرات الحالة ، وبالتالي فإن تبديل الذات (مثل extal_eval () و class_eval () do) لن يحدث أي فرق عندما يتعلق الأمر بالوصول إليها.
بشكل عام ، قد يكون من الجيد تجنب متغيرات الفصل تمامًا.
نصائح أخرى
تعديل: تم اختبار الرمز أدناه مع 1.8.7 و 1.9.1 ... يبدو أن الموقف مختلف مرة أخرى مع 1.9.2:/
الوضع في الواقع ليس ذلك مستقيمًا للأمام. هناك اختلافات في السلوك اعتمادًا على ما إذا كنت تستخدم 1.8 أو 1.9 وما إذا كنت تستخدم class_eval
أو instance_eval
.
الأمثلة أدناه تفاصيل السلوك في معظم المواقف.
لقد قمت أيضًا بتضمين سلوك الثوابت ، من أجل جيد ، لأن سلوكهم يشبه ، ولكن ليس بالضبط متغيرات الطبقة.
متغيرات الفصل
class_eval
في روبي 1.8:
class Hello
@@foo = :foo
end
Hello.class_eval { @@foo } #=> uninitialized class variable
class_eval
في روبي 1.9:
Hello.class_eval { @@foo } #=> :foo
لذلك المتغيرات الفئة نكون نظرت في 1.9 (ولكن ليس في 1.8) عند استخدام class_eval
instance_eval
في روبي 1.8 و 1.9
Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable
يبدو متغيرات الفصل ليس نظرت في 1.8 أو 1.9 عند استخدام instance_eval
ما هو مثير للاهتمام أيضًا الثوابت:
الثوابت
class_eval
في روبي 1.8
class Hello
Foo = :foo
end
Hello.class_eval { Foo } #=> uninitialized constant
class_eval
في روبي 1.9
Hello.class_eval { Foo } #=> :foo
لذلك ، كما هو الحال مع متغيرات الطبقة ، يتم النظر إلى الثوابت في 1.9 ولكن ليس في 1.8 ل class_eval
instance_eval
في روبي 1.8
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant
instance_eval
في روبي 1.9
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo
يبدو أن البحث المستمر ليس مشابهًا تمامًا لمتغير الفئة البحث عن Ruby 1.9. أ Hello
نموذج يفعل يمكنك الوصول إلى الثابت بينما Hello
الفصل لا.
حسنًا ، ربما تكون أفضل إجابة هي "فقط لأن": extal_eval باختصار يخلق نوعًا من singleton proc تم استدعاؤه مع ربط كائن معين. أوافق على أن هذا يبدو غريباً بعض الشيء ، ولكن هذا ما هو عليه.
إذا قمت بتنفيذ extal_eval بسلسلة ، فستحصل على تحذير من أن طريقتك تحاول الوصول إلى متغير الفئة:
irb(main):038:0> Test.new.instance_eval "@@a"
(eval):1: warning: class variable access from toplevel singleton method
NameError: (eval):1:in `irb_binding': uninitialized class variable ...
روبي 2.1
هذه هي الطريقة الأكثر إيجازًا وصحيحة بشكل دلالي الذي وجدته للوصول إلى متغير الفصل:
class Hello
@@foo = :foo_value
end
Hello.class_variable_get :@@foo #=> :foo_value