define_method inside instance_eval



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

例如)

class A
end
A.instance_eval do
  def method; end
end
A.method #works

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

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的singleton类,事实上这就是真正的

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

因此,这证明了规则

instance_eval中的defself的singleton类中定义了一个方法。

这与你所看到的完全一致。

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>

记住这个:

self更改为 current class更改为
类_值 接收器 接收器
instance_eval 接收器 接收者的单例类

用于:

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打开类A,方法mA上定义。接下来,调用方法define_method,它在其接收器A上创建实例方法:n

假设我们使用模块#class_eval而不是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

并且这里实例方法可以用CCD_ 19或CCD_。

最新更新