我正在研究某些代码并发现他已经使用类似的东西进行了类评估
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才能访问它
答案 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允许你这样做。有些语言认为虽然封装很好,但不应严格执行。如果由于某种原因,程序员想要违反封装,他们应该有这样的自由。