Ruby MetaProgramming中令人困惑的案例

我是Ruby MetaProgramming的初学者。 在irb中练习我的代码时,我遇到了这个问题。

class A; end a = A.new b = class < works for b.foo b.instance_eval { define_method :bar do; puts 'bar'; end } # => WHY this one works for a.bar rather than b.bar 

最后一段代码片段让我困惑。


感谢您的具体答案,但也许我没有清楚地解释我的困惑。 我真正想要理解的是为什么define_method在这些情况下表现得如此不同,这里:

 class A def foo1 p 'foo1 from A' end define_method :bar1 do p 'bar1 from A' end end a = A.new a.foo1 # => 'foo1 from A' a.bar1 # => 'bar1 from A' a.instance_eval { def foo2; p 'foo2 from a.metaclass'; end } a.foo2 # => 'foo2 from a.metaclass' a.instance_eval { define_method :bar2 do; p 'bar2 from a.metaclass'; end } # => NoMethodError: undefined method `define_method' for # aa = class < 'foo3 from a.metaclass.metaclass' aa.instance_eval { define_method :bar3 do; p 'bar3 from a.metaclass.metaclss'; end } aa.bar3 # => NoMethodError: undefined method `bar3' for #<Class:#> a.bar3 # => 'bar3 from a.metaclass.metaclss' 

我知道这不会出现在日常编码中,但我想让我的想法清楚。


得出结论:

 aa = class < 'foo...' aa.instance_eval { define_method :bar do; puts 'bar..'; end } # equals aa.class_eval { def bar; puts 'bar..'; end } # both define a singleton-method for a, # as define_method and class_eval both define instance_method a.bar # => 'bar...' 

除了所有其他评论:

[来自Pickaxe]方法Object#instance_eval允许您将self设置为某个任意对象,使用[self]计算块中的代码,然后重置self。
和Module#define_method:定义接收者中的实例方法[self,必须是(匿名)类或模块]。

 singleton_class_of_object_a = aa = class << a; self; end aa.instance_eval { def foo3; puts "foo3 from singleton class of a, self=#{self}"; end } aa.foo3 # => foo3 from singleton class of a, self=#> aa.instance_eval do puts "about to define_method :bar3 in self=#{self}" define_method :bar3 do; puts "bar3 from singleton class of a, self=#{self}"; end end # => about to define_method :bar3 in self=#> a.bar3 # => bar3 from singleton class of a, self=# 

define_method :bar3在singleton_class_of_object_a(一个匿名类,见下文)的上下文中执行,因此定义了该类的实例方法,因此bar3成为a的单例方法。 正如我在之前的回答中所说,它等同于直接在对象上定义:

 def a.bar4; puts 'bar4 from singleton class of a' end a.bar4 # => bar4 from singleton class of a p a.singleton_methods.sort # => [:bar3, :bar4, :foo2] p a.methods(false).sort # => [:bar3, :bar4, :foo2] 

a = A.new之后,实例a的字段类指向A类。
使用class << adef a.bar4 ,Ruby创建一个匿名类,实例a的字段类现在指向此匿名类 ,并从那里指向A。
在此上下文中使用defdefine_method的方法将进入匿名类的方法表。

因为在你的情况下, def foo充当def self.foo
define_method :bar充当def bar

发生这种情况会导致instance_eval创建类方法

你的代码与:

 class << a def self.foo puts 'foo' end def bar puts 'bar' end end 

所以foo方法是在a特征类中定义的
bar方法在自身内部定义。

如果你需要a.foo工作,请使用class_eval

它会起作用,因为class_eval会创建实例方法

 b.class_eval { def foo; puts 'foo'; end } a.foo # => foo 
 b.instance_eval { def foo; puts 'foo'; end } b.instance_eval { puts "self in b.instance_eval block=#{self}" } #=> self in b.instance_eval block=#> b.foo #=> foo 

您正在单个A实例的单例类中定义一个方法。它看起来非常复杂。 而是直接在实例上定义单例方法:

 cat = String.new("cat") def cat.speak 'miaow' end cat.speak #=> "miaow" cat.singleton_methods #=> ["speak"] 

class <<符号如

 singleton_class_of_A = eigenclass_of_A = class << A; self; end 

通常用于定义“类方法”(实际上是A的单例方法),或类的实例变量:

 class B class << self def my_class_method_of_B puts "my_class_method_of_B" end @my_first_variable_of_class_B = 123 attr_accessor :my_second_variable_of_class_B end end B.my_class_method_of_B print 'singleton methods of B : ' p B.singleton_methods print 'instance variables of B : ' p B.instance_variables print 'class variables of B : ' p B.class_variables print '"singleton variables" of B : ' class << B; p instance_variables end 

输出:

 my_class_method_of_B singleton methods of B : [:my_class_method_of_B, :my_second_variable_of_class_B, :my_second_variable_of_class_B=] instance variables of B : [] class variables of B : [] "singleton variables" of B : [:@my_first_variable_of_class_B] 

如您所见,这件事并不容易,您可以查看http://pragprog.com/book/ppmetr/metaprogramming-ruby