سؤال

لدي ما يلي:

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top