2

当我在 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

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

4

3 回答 3

4

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

class A
end

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

在哪里foo定义?我能想到的唯一明智的地方是a'ssingleton 类,确实如此

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

所以这证明了规则

definside在的单例类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) {}

这是创建普通实例方法的元编程方式。这种不一致可能看起来很烦人,但再看看正常情况下的情况,你就会明白为什么def并且define_method 不能保持一致。这

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

真的只是

a.define_method(:foo) {}

这是胡说八道

NoMethodError: undefined method `define_method' for #<A:0x00008>
于 2015-06-23T21:49:57.157 回答
1

记住这一点:

更改self 更改current class
class_eval 接收者 接收者
instance_eval 接收者 接收者的单例类

在 ruby​​ 中用def关键字定义的方法(没有明确的接收者)总是在“当前类”中定义(这就是 Paolo Perrotta 在Metaprogramming Ruby中称之为;其他人称之为“默认定义”)

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

但是,当您调用 instance_eval 且A类为接收者时,根据上表,您正在将“当前类”更改为接收者的单例类;由于您使用 定义方法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)中。

于 2020-12-28T04:55:37.003 回答
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,方法m定义在A. 接下来,方法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

这里的实例方法可以用def或定义define_method

于 2015-06-23T22:01:22.700 回答