class MyClass
def one
def two
end
end
end
obj = MyClass.new
obj.one
puts obj.method(:two).owner #==> MyClass
这里我在另一个方法中定义方法二。方法一由MyClass(obj)的实例调用。因此,当定义方法二时,self是obj。当我检查方法二的所有者时,它是MyClass
obj.instance_eval do
def three
end
end
puts obj.method(:three).owner #==> #<Class:#<MyClass:0x007f85db109010>>
在这个片段中,我在obj上执行instance_eval,所以当定义方法三时,self再次成为obj。但是当我检查三个人的主人时,它是obj的单身类
这是为什么?除了self之外还有什么能决定方法定义的位置吗?答案 0 :(得分:9)
我会用Kernel#set_trace_func
方法向你解释幕后发生了什么。首先看下面的代码和输出:
trace = lambda do |event,file,line,id,binding,klass|
p [event,File.basename(file),line,id,binding,klass]
end
set_trace_func trace
class MyClass
def self.bar;end
def one
def two
end
end
end
obj = MyClass.new
obj.one
obj.instance_eval do
def three
end
end
<强>输出:强>
-----------------
----------------
-----------------
-----------------
-----------------
----------------- # part A
["c-call", "test.rb", 9, :singleton_method_added, #<Binding:0x83ab2b0>, BasicObject]
["c-return", "test.rb", 9, :singleton_method_added, #<Binding:0x83aaeb4>, BasicObject]
["line", "test.rb", 10, nil, #<Binding:0x83aab80>, nil]
["c-call", "test.rb", 10, :method_added, #<Binding:0x83aa900>, Module]
["c-return", "test.rb", 10, :method_added, #<Binding:0x83aa07c>, Module]
----------------------------- # part B
["line", "test.rb", 16, nil, #<Binding:0x83a976c>, nil]
["c-call", "test.rb", 16, :new, #<Binding:0x83a9488>, Class]
["c-call", "test.rb", 16, :initialize, #<Binding:0x83a90a0>, BasicObject]
["c-return", "test.rb", 16, :initialize, #<Binding:0x83a8e20>, BasicObject]
["c-return", "test.rb", 16, :new, #<Binding:0x83a8b28>, Class]
---------------------------
---------------------------
--------------------------- # part C
["c-call", "test.rb", 11, :method_added, #<Binding:0x83a7de0>, Module]
["c-return", "test.rb", 11, :method_added, #<Binding:0x83a79f8>, Module]
--------------------------- # part D
["line", "test.rb", 18, nil, #<Binding:0x83a7034>, nil]
["c-call", "test.rb", 18, :instance_eval, #<Binding:0x83a6c10>, BasicObject]
["line", "test.rb", 19, nil, #<Binding:0x83a65f8>, nil]
["c-call", "test.rb", 19, :singleton_method_added, #<Binding:0x83a61d4>, BasicObject]
["c-return", "test.rb", 19, :singleton_method_added, #<Binding:0x83a5ef0>, BasicObject]
["c-return", "test.rb", 18, :instance_eval, #<Binding:0x83a5d4c>, BasicObject]
<强>解释强>
查看 A部分下面的 5 行。它只是告诉我们Ruby何时会在类中找到def
关键字,它会将该方法作为实例方法添加到该类中。这是通过调用钩子方法Module#method_added
来完成的。同样的解释分为 C部分下面的两行。
现在obj.instance_eval {..}
内发生了什么?
好的,如果你看一下 D部分下面的行,这将被清除。从最后开始,第一个和第二个行。在instance_eval
块def third
旁边,third
会将singleton_method
添加为{对象ob
的{1}},通过调用钩子方法BasicObject#singleton_method_added
。
这就是 MRI 的写法。
答案 1 :(得分:2)
def
不是一种方法,因此在self
时,它不需要像方法那样表现。令人困惑,因为这两者显然是等价的:
class Foo
def one
"one"
end
define_method(:two) { "two" }
end
虽然这两个显然不是(Bar的实例没有define_method
)
class Bar
def one
def two
"two"
end
"one"
end
def three
define_method(:four) { "four" }
"three"
end
end
您可以将此视为论证嵌套def
归类的原因。这是有道理的,因为当你打开类范围时,这种嵌套的def
只能是 ,所以它会影响每个实例。
另一方面,def
中的instance_eval
被添加到单例类中是绝对有意义的,因为您只显式打开了实例。它会破坏封装,使其与其他情况一样工作。
所以...基本上是它的特殊行为。但这并非完全没有意义。
答案 2 :(得分:2)
ruby-core贡献者yugui的一篇很好的文章解释了这一点:Three implicit contexts in Ruby。基本上,有一个默认的定义上下文,不与self
相同。未明确定义为单例方法的方法最终作为默认定义上下文的实例方法。 module
和class
定义主体更改默认定义上下文,而def
则不会。 instance_eval
OTOH 会更改它。