Pergunta

Eu tenho o seguinte:

class Test
    @@a = 10

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

    class << self
      @@b = '40'

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

Por que o seguinte trabalho:

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

Mas eu não consigo acessar @@b diretamente?

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

Da mesma forma, as seguintes obras

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

mas o seguinte falha

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

Eu não entendo por que eu não consigo acessar as Variáveis de Classe, diretamente a partir do instance_eval blocos.

Foi útil?

Solução

Eu só pedi a mesma pergunta para Matz durante o RubyKaigi festa.Eu estava meio bêbado, mas ele estava perfeitamente sóbria, de modo que você pode tomar isso como a resposta definitiva.

Anton é certo, a razão pela qual você não é possível acessar variáveis de classe através de instance_eval() é "apenas porque".Mesmo class_eval() tem o mesmo problema (Matz si mesmo não estava totalmente certo sobre class_eval() até que eu disse a ele que eu já tentei).Mais especificamente:escopo-sábio, variáveis de classe são mais constantes do que variáveis de instância, de modo de comutação auto (como instance_eval() e class_eval() não) não vai fazer qualquer diferença quando se trata de aceder a eles.

Em geral, pode ser uma boa idéia para evitar variáveis de classe completamente.

Outras dicas

EDITAR: abaixo o código foi testado com 1.8.7 e 1.9.1...parece que a situação é diferente novamente com 1.9.2 :/

A situação realmente não é reta para a frente.Há diferenças no comportamento, dependendo se você estiver usando o 1.8 ou 1.9 e se você está usando class_eval ou instance_eval.

Os exemplos a seguir descrevem o comportamento na maioria das situações.

Eu também incluiu o comportamento de constantes, para uma boa medida, como o seu comportamento é semelhante, mas não exatamente o mesmo, variáveis de classe.

Variáveis de classe

class_eval em Ruby 1.8:

class Hello
    @@foo = :foo
end

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

class_eval no Ruby 1.9:

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

Assim, variáveis de classe são olhou em 1,9 (mas não em 1,8) quando usar class_eval

instance_eval em Ruby 1.8 e 1.9

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

Parece variáveis de classe são não olhou em 1,8 ou 1.9 quando utilizar instance_eval

O que é interessante também é o caso para constantes:

Constantes

class_eval em Ruby 1.8

class Hello
    Foo = :foo
end

Hello.class_eval { Foo } #=> uninitialized constant

class_eval no Ruby 1.9

Hello.class_eval { Foo } #=> :foo

Assim, como com a classe de variáveis, constantes são procurados em 1.9, mas não em 1.8 por class_eval

instance_eval em Ruby 1.8

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

instance_eval no Ruby 1.9

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

Parece que a constante de pesquisa não é bastante análoga à variável de classe de olhar para cima para o Ruby 1.9.Um Hello instância não obtenha acesso a uma constante enquanto a Hello classe não.

Bem, provavelmente, a melhor resposta é "apenas porque":o instance_eval em poucas palavras cria algum tipo de singleton proc que é invocado com a vinculação de um determinado objeto.Eu concordo que é que soa um pouco estranho, mas é o que é.

Se você executar instance_eval com uma seqüência de caracteres, você ainda vai receber um aviso de que seu método tenta acessar a variável 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

Este é o mais conciso e semanticamente correto maneira que eu encontrei para acessar uma variável de classe:

class Hello
    @@foo = :foo_value
end

Hello.class_variable_get :@@foo  #=> :foo_value
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top