instance_eval中的define_method

时间:2015-06-23 21:08:41

标签: ruby instance-eval

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

Eg)的

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

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

3 个答案:

答案 0 :(得分:2)

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

class A
end

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

foo在哪里定义?我能想到的唯一合理的地方是a的单身人士,确实是真的

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

所以这证明了规则

  def内的{p> instance_eval定义了self的单例类中的方法。

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

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>

答案 1 :(得分:0)

有关:

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,在其接收方:n上创建实例方法A

假设我们使用Module#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

此处可以使用defdefine_method来定义实例方法。

答案 2 :(得分:0)

记住这一点:

<头>
self 更改为 current class 更改为
class_eval 接收器 接收器
instance_eval 接收器 接收者的单例类

在 ruby​​ 中使用 def 关键字(没有显式接收器)定义的方法总是在“当前类”中定义(这就是 Paolo Perrotta 在 Metaprogramming Ruby 中所称的;其他人称之为"default definee")

“当前类”是我们通常不会考虑的东西,因为它很直观;在类定义中,当前类是类本身,因此在类定义中使用 def foo 定义的方法成为该类的实例方法。

但是当您使用 A 类作为接收器调用 instance_eval 时,根据上表,您将“当前类”更改为接收器的单例类;由于您使用 def 定义方法,因此它将在“当前类”中定义,从而产生“类方法”(在 A 的单例类或特征类中定义的方法):

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 定义方法时,根据上表,您实际上是在隐式接收器 define_method 上调用 self;如果您查看表格,self 将是接收方 A,因此它与调用 A.define_method 甚至:

class A

  define_method(:method1) {}

end

这就是为什么,在这种情况下,您会得到一个实例方法,因为您在类 A 上调用了define_method,而不是在 A 的单例类(或也称为 eigenclass)中。