6

instance_eval我了解和之间的基本区别class_eval。我在玩耍时发现了一些奇怪的东西attr_accessor。这是一个例子:

A = Class.new
A.class_eval{ attr_accessor :x }

a = A.new
a.x = "x"
a.x
=> "x"  # ... expected

A.instance_eval{ attr_accessor :y }

A.y = "y"
=> NoMethodError: undefined method `y=' for A:Class

a.y = "y"
=> "y"      # WHATTT?

这是怎么回事:

  1. instance_eval 没有访问我们的 A 类(对象)
  2. 然后它实际上将它添加到 A 的实例中?
4

4 回答 4

9

一开始,你的理解(或直觉)是正确的,里面定义的方法#instance_eval#class_eval不一样

A = Class.new

A.instance_eval { def defined_in_instance_eval; :instance_eval; end }
A.class_eval { def defined_in_class_eval; :class_eval; end }

A.new.defined_in_class_eval # => :class_eval
A.defined_in_instance_eval # => :instance_eval

附注:虽然self两者相同instance_evalclass_eval默认定义不同,请参阅http://yugui.jp/articles/846

真正的诀窍是Module#attr_accessor它本身,看看它的定义: http ://rxr.whitequark.org/mri/source/vm_method.c#620

它不使用def,不读取上下文self或默认定义者。它只是“手动”将方法插入到模块中。这就是为什么结果是违反直觉的。

于 2013-01-21T02:28:57.650 回答
2

class_eval和的区别instance_eval动态创建类方法

class A; end
A.class_eval do
    attr_accessor :x
    def barx; end
    define_method :foox do; end
end

print 'A.instance_methods  : '; p A.instance_methods(false).sort
print 'A.singleton_methods : '; p A.singleton_methods

class B; end
B.instance_eval do
    attr_accessor :y
    def bary; end
    define_method :fooy do; end
end

print 'B.instance_methods  : '; p B.instance_methods(false).sort
print 'B.singleton_methods : '; p B.singleton_methods

class C; end
singleton_class = class << C; self end
singleton_class.instance_eval do
    attr_accessor :z
    def barz; puts 'where is barz ?' end
    define_method :fooz do; end
end

print 'C.instance_methods  : '; p C.instance_methods(false).sort
print 'C.singleton_methods : '; p C.singleton_methods

print 'singleton_class.barz : '; singleton_class.barz
print 'singleton_class.methods  : '; p singleton_class.methods(false)

输出(红宝石 1.8.6):

A.instance_methods  : ["barx", "foox", "x", "x="]
A.singleton_methods : []
B.instance_methods  : ["fooy", "y", "y="]
B.singleton_methods : ["bary"]
C.instance_methods  : []
C.singleton_methods : ["z", "z=", "fooz"]
singleton_class.barz : where is barz ?
singleton_class.methods  : ["barz"]

正如您在 B 中看到的那样,尽管 instance_eval 通常创建单例方法,但显然attr_accessordefine_method强制定义实例方法。

于 2013-01-21T00:56:20.893 回答
1
A.singleton_class.class_eval { attr_accessor :y }
A.y = 'y'
A.y
于 2013-01-21T06:09:11.997 回答
0

该方法attr_accessor是一种类方法,当在类的主体中调用时访问器方法会在该类的实例上定义。

当你这样做时,你是在一个类的主体A.class_eval{...}中调用它,所以它的实例被分配了访问器。 Aa

当你这样做时,你在一个类的非主体A.instance_eval{...}调用它,所以它的实例没有被分配访问器。 A

如果你这样做了,那么你就是在一个类的主体Class.class_eval{attr_accessor :z}中调用它,所以它的实例例如将被分配访问器:。 ClassAA.z = ...

于 2013-01-21T00:55:50.397 回答