define_method 里面 instance_eval

define_method inside instance_eval

当我在 instance_eval 块中为 class 定义一个方法时,它创建了一个很好的 class 方法。

例)

class A
end

A.instance_eval do
  def method; end
end

A.method #works

但是当我在 instance_eval 中使用 define_method 时,它会创建实例方法而不是 class 方法 例如)

A.instance_eval do
  define_method(:method1) {}
end
A.method1 # NoMethodError: undefined method `method1'
A.new.method1 # Works fine

以上现象我无法理解。请有人帮帮我。

如果您在 实例 (这是它的主要目的)的上下文中查看 instance_eval,这种古怪的行为会更有意义。

class A
end

a = A.new
a.instance_eval do
  def foo
  end
end

foo在哪里定义的?我能想到的唯一明智的地方就是a的单例class,确实如此

a.method(:foo).owner == a.singleton_class
# true

所以这证明了规则

def inside instance_eval defines a method in self's singleton class.

这和你看到的完全一致。

A.instance_eval do
  # defines method in A's singleton class!
  def method; end
end

那么为什么 define_method 表现不同呢?因为与 def 不同,它是 方法 !所以这个

A.instance_eval do
  define_method(:foo) {}
end

真的只是

A.define_method(:foo) {}

这是创建普通实例方法的元编程方式。这种不一致可能看起来很烦人,但再看看正常实例的情况,您就会明白为什么 defdefine_method 不能 一致。这个

a.instance_eval do
  define_method(:foo) {}
end

真的只是

a.define_method(:foo) {}

胡说八道

NoMethodError: undefined method `define_method' for #<A:0x00008>

对于:

class A; end

A.instance_eval do
  puts "self=#{self}"
  def m; puts "hi"; end
  define_method(:n) {puts "ho" }
end
  #=> "self=A"

我们发现:

A.methods(false)          #=> [:m] 
A.instance_methods(false) #=> [:n] 
A.m                       # hi
A.n                       # NoMethodError:...
A.new.m                   # NoMethodError:...
A.new.n                   # ho

A.instance_eval 打开 class A 并且在 A 上定义了方法 m。接下来,调用方法 define_method,它在其接收者 A.

上创建实例方法 :n

假设我们要使用 Module#class_eval rather than BasicObject#instance_eval:

A.class_eval do
  puts "self=#{self}"
  def m; puts "hi"; end
  define_method(:n) {puts "ho" }
end
  #=> "self=A"

我们发现:

A.methods(false)          #=> [] 
A.instance_methods(false) #=> [:m, :n] 
A.m                       # NoMethodError:...
A.n                       # NoMethodError:...
A.new.m                   # hi
A.new.n                   # ho

所以你看到这个行为是一样的:

class A
  puts "self=#{self}"
  def m; puts "hi"; end
  define_method(:n) {puts "ho" }
end

这里的实例方法可以用 defdefine_method.

定义

记住这个:

changes self to changes current class to
class_eval receiver receiver
instance_eval receiver receiver's singleton class

在 ruby 中使用 def 关键字(没有明确的接收者)定义的方法总是在“当前 class” 中定义(这就是 Paolo Perrotta 在 元编程Ruby;别人叫它“default definee”)

“current class”是我们通常不会考虑的东西,因为它很直观;在 class 定义中,当前 class 是 class 本身,因此在 class 定义中用 def foo 定义的方法成为 [=49] 的实例方法=].

但是当你调用 instance_eval 并且 A class 作为接收者时,根据上面的 table,你正在将 'current class' 更改为接收者的单例class;由于您正在使用 def 定义方法,因此它将在 'current class' 中定义,这将导致 'class method'(在 A 的单例 class 或 eigen[= 中定义的方法49=]):

class A
end

A.instance_eval do
  # the current class here is not A, but A's singleton class; 
  def method; end
end

但是,当您使用 define_method 定义方法时,按照上面的 table,您实际上是在隐式接收器 self 上调用 define_method;如果你查看 table,self 将是接收者 A,因此它与调用 A.define_method 甚至:

相同
class A

  define_method(:method1) {}

end

所以这就是为什么,在这种情况下,你得到一个实例方法,因为你在 class A 上调用了 define_method,而不是在 A 的单例 class 中调用(或者也称为本征class).