需要帮助才能理解这段代码

时间:2013-08-08 06:11:40

标签: ruby metaprogramming

我正在研究某些代码并发现他已经使用类似的东西进行了类评估

self.class_eval("@default_robot_engine = RobotEngine.new(some_block)")

以后可以像这样访问

self.class_eval("@default_robot_engine")

我需要帮助来理解这段代码。有没有其他方法可以访问@default_robot_engine而不是在其上执行class_eval?

当我做Class.instance_variable_names时我得到了

[ “@ attribute_methods_mutex”,  “@generated_attribute_methods”  “@generated_feature_methods”  “@observer_instances”  “@每页”,  “@父母名字”,  “@registered_robot_engines”  “@default_robot_engine”  “@首要的关键”,  “@quoted_primary_key”  “@locking_column”  “@attribute_methods_generated”  “@table_name”  “@quoted_table_name”  “@arel_table”  “@arel_engine”  “@关系”,  “@列”,  “@column_names”  “@columns_hash”  “@cached_attributes”  “@attribute_method_matchers_cache”  “@generated_external_attribute_methods”]

我可以访问除ClassName.registered_robot_engine之外的所有实例变量default_robot_engine。为什么呢?

好的,我得到了答案,因为这个实例变量是一个动态变量,并且没有设置attr_reader,所以我认为只有通过class_eval才能访问它

4 个答案:

答案 0 :(得分:2)

这是一段特别奇怪的代码。首先,self.class_eval根本不需要。普通class_eval会做得恰到好处。我想程序员更多地使用其他语言而不是Ruby。在Ruby中,仅在极少数情况下使用显式self接收器,例如在调用以=符号结尾的方法时,或者在确保调用的方法是公共方法时(私有方法在调用时将失败)有明确的接收者)。

其次,很难想象程序员为什么不使用标准的getter和setter,如:

class << self
  attr_accessor :default_robot_engine
end

# Here is the case when its legal to use explicit self receiver:
self.default_robot_engine = RobotEngine.new( some_block )

稍后只需通过

访问它
default_robot_engine

我强烈怀疑原来的程序员不了解Ruby基础知识。即使有人有理由在没有定义访问器的情况下篡改实例变量,但最好不要通过class_eval,使用#instance_variable_get/set方法购买:

instance_variable_set :@default_robot_engine, RobotEngine.new( some_block )
instance_variable_get :@default_robot_engine

对于这种情况,我认为班级评估太大了。

答案 1 :(得分:1)

哇,这很有趣。

1.9.3-p429 :094 > class C; self.class_eval "a=3;@b=4;@@c=5"; end
 => 5 
1.9.3-p429 :095 > C.class_variables
 => [:@@c] 
1.9.3-p429 :096 > class C; puts self.class_eval "a+@b+@@c"; end
NameError: undefined local variable or method `a' for C:Class
from (irb):96:in `class_eval'
from (irb):96:in `class_eval'
from (irb):96:in `<class:C>'
from (irb):96
from /Users/cphoenix/.rvm/rubies/ruby-1.9.3-p429/bin/irb:16:in `<main>'
1.9.3-p429 :097 > class C; puts self.class_eval "@b+@@c"; end
9
 => nil 
1.9.3-p429 :098 > 
1.9.3-p429 :098 > C.object_id
 => 2151815060 
1.9.3-p429 :099 > C.class_eval "puts self.object_id"
2151815060
 => nil 
1.9.3-p429 :100 > 

这是似乎正在发生的事情。当您执行C.class_eval时,您正在该类的上下文中执行代码;自我是阶级。

当你说C.class_variables时,它打印出看起来像类变量的东西。这只是我在第094行定义的三个变量中的@@ c。

所以我猜这个self.class_eval是一种只用一个@而不是两个来定义一个类变量的方法。

我不知道为什么+ @ b + @@ c找不到,但@b + @@ c确实找到了这两个变量。所以我想这只是一个部分答案......我不确定@b是否存储在与@@ c不同的地方,我不知道会发生什么。

这可能只是Ruby的怪异。

答案 2 :(得分:0)

尝试阅读并理解这一个:

http://www.jimmycuadra.com/posts/metaprogramming-ruby-class-eval-and-instance-eval

非常有用。

答案 3 :(得分:0)

  

我可以像这样访问所有实例变量   除default_robot_engine之外的ClassName.registered_robot_engine。为什么呢?

class Dog
  class<< self
    attr_accessor :registered_robot_engine

    def set_stuff
      @registered_robot_engine = 'hello'
      @default_robot_engine = 20
    end
  end
end

Dog.set_stuff
puts Dog.registered_robot_engine
puts Dog.default_robot_engine

--output:--
hello
1.rb:16:in `<main>': undefined method `default_robot_engine' for Dog:Class (NoMethodError)

ruby​​中的基本规则是默认情况下所有实例变量都是私有的,因此除非您为实例变量提供访问器方法,否则无法访问它。在上面的示例中,没有为@default_robot_engine定义的访问器方法,因此它是不可访问的,而另一个实例变量确实具有为其定义的访问器方法,因此可以访问它。

class_eval()和instance_eval()都允许您违反封装并读取或写入私有实例变量:

class Dog
  class <<self
    def set_stuff
      @registered_robot_engine = 'hello'
      @default_robot_engine = 20
    end

    def set_more_stuff
      class_eval do 
        @default_robot_engine = 100 
      end
    end
  end
end

Dog.set_stuff
Dog.set_more_stuff

puts Dog.class_eval{ @default_robot_engine }

--output:--
100

instance_variable_set()和instance_variable_get()允许你做同样的事情:

class Dog
  def initialize
    @name = "Rover"
  end
end

d = Dog.new
d.instance_variable_set(:@name, "John")
puts d.instance_variable_get(:@name)

--output:--
John
  

其次,很难想象程序员为什么不使用标准   getter和setter,如:

class << self
  attr_accessor :default_robot_engine
end

我猜测程序员正在使用其他人的模块,并且程序员决定违反封装,ruby允许你这样做。有些语言认为虽然封装很好,但不应严格执行。如果由于某种原因,程序员想要违反封装,他们应该有这样的自由。