我是Ruby MetaProgramming的初学者。当我在irb中练习我的代码时,我遇到了这个问题。
class A; end
a = A.new
b = class << a; self; end
b.instance_eval { def foo; puts 'foo'; end }
# => 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 #<A:0x000000016a2e70>
aa = class << a; self; end
aa.instance_eval { def foo3; p 'foo3 from a.metaclass.metaclass'; end }
aa.foo3 # => '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:0x000000016a2e70>>
a.bar3 # => 'bar3 from a.metaclass.metaclss'
我知道这不会出现在日常编码中,但我想让我的想法清楚。
得出结论:
aa = class << a; self; end
aa.instance_eval { def foo; puts 'foo..'; end }
# defines a singleton-method for aa
aa.foo # => '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...'
答案 0 :(得分:2)
除了所有其他评论:
[来自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=#<Class:#<A:0x007fc2e4049e68>>
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=#<Class:#<A:0x007fc2e4049e68>>
a.bar3 # => bar3 from singleton class of a, self=#<A:0x007fc2e4049e68>
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 << a
或def a.bar4
,Ruby会创建一个匿名类,实例 a 的字段类现在指向此匿名类,并从那里到的 A 即可。
在此上下文中使用def
或define_method
定义的方法将进入匿名类的方法表。
答案 1 :(得分:1)
因为在您的情况下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
本身内定义。
如果您需要a.foo
使用class_eval
代替。
它会起作用,因为 class_eval
会创建实例方法:
b.class_eval { def foo; puts 'foo'; end }
a.foo
# => foo
答案 2 :(得分:1)
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=#<Class:#<A:0x007fe3c204d000>>
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