Question

J'ai ce qui suit:

class Test
    @@a = 10

    def show_a()
        puts "a: #{@@a}"
    end

    class << self
      @@b = '40'

      def show_b
        puts "b: #{@@b}"
    end
  end
end

Pourquoi le travail suivant:

Test.instance_eval{show_b}
b: 40
=> nil

Mais je ne peux pas accéder @@b directement?

Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object

De même, le suivant fonctionne

t = Test.new
t.instance_eval{show_a}
a: 10
=> nil

Mais ce qui suit échoue

t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object

Je ne comprends pas pourquoi je ne peux pas accéder aux variables de classe directement à partir du instance_eval blocs.

Était-ce utile?

La solution

Je viens de poser la même question à Matz lors de la fête Rubykaigi. J'étais à moitié dessinée, mais il était parfaitement sobre, donc vous pouvez le considérer comme la réponse définitive.

Anton a raison - la raison pour laquelle vous ne pouvez pas accéder aux variables de classe via instance_eval () est "simplement parce que". Même class_eval () partage le même problème (Matz lui-même n'était pas totalement sûr de class_eval () jusqu'à ce que je lui dise que je l'avais déjà essayé). Plus précisément: en termes d'étendue, les variables de classe ressemblent plus à des constantes que les variables d'instance, donc le changement de soi (comme instance_eval () et class_eval () ne fera pas de différence lorsqu'il s'agit d'y accéder.

En général, il pourrait être une bonne idée d'éviter complètement les variables de classe.

Autres conseils

ÉDITER: Le code ci-dessous a été testé avec 1.8.7 et 1.9.1 ... il semble que la situation soit à nouveau différente avec 1.9.2: /

La situation n'est en fait pas si simple. Il existe des différences de comportement selon que vous utilisez 1.8 ou 1.9 et si vous utilisez class_eval ou instance_eval.

Les exemples ci-dessous détaillent le comportement dans la plupart des situations.

J'ai également inclus le comportement des constantes, pour faire bonne mesure, car leur comportement est similaire, mais pas exactement la même chose que les variables de classe.

Variables de classe

class_eval Dans Ruby 1.8:

class Hello
    @@foo = :foo
end

Hello.class_eval { @@foo } #=> uninitialized class variable

class_eval Dans Ruby 1.9:

Hello.class_eval { @@foo } #=> :foo

Donc les variables de classe sommes Regardant les yeux en 1.9 (mais pas en 1,8) lors de l'utilisation class_eval

instance_eval Dans Ruby 1.8 et 1.9

Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable

Il semble que les variables de classe soient ne pas levé les yeux en 1.8 ou 1.9 Lors de l'utilisation instance_eval

Ce qui est également intéressant, c'est le cas pour constantes:

Constantes

class_eval Dans Ruby 1.8

class Hello
    Foo = :foo
end

Hello.class_eval { Foo } #=> uninitialized constant

class_eval Dans Ruby 1.9

Hello.class_eval { Foo } #=> :foo

Donc, comme pour les variables de classe, les constantes sont recherchées en 1,9 mais ne pas en 1,8 pour class_eval

instance_eval Dans Ruby 1.8

Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant

instance_eval Dans Ruby 1.9

Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo

Il semble que la recherche constante n'est pas tout à fait analogue à la recherche de variables de classe pour Ruby 1.9. UN Hello exemple Est-ce que avoir accès à la constante pendant que le Hello La classe ne le fait pas.

Eh bien, la meilleure réponse est probablement "juste parce que": l'instance_eval en un mot crée une sorte de proc singleton qui est invoqué avec la liaison d'un objet donné. Je suis d'accord que cela semble un peu étrange, mais c'est ce que c'est.

Si vous exécutez instance_eval avec une chaîne, vous obtiendrez même un avertissement que votre méthode essaie d'accéder à la variable de classe:

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 ...

Ruby 2.1

C'est la manière la plus concise et le plus sémantique que j'ai trouvé pour accéder à une variable de classe:

class Hello
    @@foo = :foo_value
end

Hello.class_variable_get :@@foo  #=> :foo_value
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top